@learncard/sss-key-manager 0.1.21 → 0.1.22

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"),
@@ -147,6 +149,16 @@ var require_types_cjs_development = __commonJS({
147
149
  CredentialInfoValidator: /* @__PURE__ */ __name(() => CredentialInfoValidator, "CredentialInfoValidator"),
148
150
  CredentialNameRefValidator: /* @__PURE__ */ __name(() => CredentialNameRefValidator, "CredentialNameRefValidator"),
149
151
  CredentialRecordValidator: /* @__PURE__ */ __name(() => CredentialRecordValidator, "CredentialRecordValidator"),
152
+ CredentialRefreshChallengeValidator: /* @__PURE__ */ __name(() => CredentialRefreshChallengeValidator, "CredentialRefreshChallengeValidator"),
153
+ CredentialRefreshFailedResultValidator: /* @__PURE__ */ __name(() => CredentialRefreshFailedResultValidator, "CredentialRefreshFailedResultValidator"),
154
+ CredentialRefreshFailureCodeValidator: /* @__PURE__ */ __name(() => CredentialRefreshFailureCodeValidator, "CredentialRefreshFailureCodeValidator"),
155
+ CredentialRefreshResponseEnvelopeValidator: /* @__PURE__ */ __name(() => CredentialRefreshResponseEnvelopeValidator, "CredentialRefreshResponseEnvelopeValidator"),
156
+ CredentialRefreshResultValidator: /* @__PURE__ */ __name(() => CredentialRefreshResultValidator, "CredentialRefreshResultValidator"),
157
+ CredentialRefreshSigningModeValidator: /* @__PURE__ */ __name(() => CredentialRefreshSigningModeValidator, "CredentialRefreshSigningModeValidator"),
158
+ CredentialRefreshUnchangedResultValidator: /* @__PURE__ */ __name(() => CredentialRefreshUnchangedResultValidator, "CredentialRefreshUnchangedResultValidator"),
159
+ CredentialRefreshUnsupportedResultValidator: /* @__PURE__ */ __name(() => CredentialRefreshUnsupportedResultValidator, "CredentialRefreshUnsupportedResultValidator"),
160
+ CredentialRefreshUpdatedResultValidator: /* @__PURE__ */ __name(() => CredentialRefreshUpdatedResultValidator, "CredentialRefreshUpdatedResultValidator"),
161
+ CredentialRefreshVersionMetadataValidator: /* @__PURE__ */ __name(() => CredentialRefreshVersionMetadataValidator, "CredentialRefreshVersionMetadataValidator"),
150
162
  CredentialSchemaValidator: /* @__PURE__ */ __name(() => CredentialSchemaValidator, "CredentialSchemaValidator"),
151
163
  CredentialStatusValidator: /* @__PURE__ */ __name(() => CredentialStatusValidator, "CredentialStatusValidator"),
152
164
  CredentialSubjectValidator: /* @__PURE__ */ __name(() => CredentialSubjectValidator, "CredentialSubjectValidator"),
@@ -167,6 +179,8 @@ var require_types_cjs_development = __commonJS({
167
179
  GeoCoordinatesValidator: /* @__PURE__ */ __name(() => GeoCoordinatesValidator, "GeoCoordinatesValidator"),
168
180
  GetCounterEventValidator: /* @__PURE__ */ __name(() => GetCounterEventValidator, "GetCounterEventValidator"),
169
181
  GetCountersEventValidator: /* @__PURE__ */ __name(() => GetCountersEventValidator, "GetCountersEventValidator"),
182
+ GetCredentialRefreshHistoryInputValidator: /* @__PURE__ */ __name(() => GetCredentialRefreshHistoryInputValidator, "GetCredentialRefreshHistoryInputValidator"),
183
+ GetCredentialRefreshHistoryResultValidator: /* @__PURE__ */ __name(() => GetCredentialRefreshHistoryResultValidator, "GetCredentialRefreshHistoryResultValidator"),
170
184
  GetFullSkillTreeInputValidator: /* @__PURE__ */ __name(() => GetFullSkillTreeInputValidator, "GetFullSkillTreeInputValidator"),
171
185
  GetFullSkillTreeResultValidator: /* @__PURE__ */ __name(() => GetFullSkillTreeResultValidator, "GetFullSkillTreeResultValidator"),
172
186
  GetSkillPathInputValidator: /* @__PURE__ */ __name(() => GetSkillPathInputValidator, "GetSkillPathInputValidator"),
@@ -191,6 +205,7 @@ var require_types_cjs_development = __commonJS({
191
205
  JWEValidator: /* @__PURE__ */ __name(() => JWEValidator, "JWEValidator"),
192
206
  JWKValidator: /* @__PURE__ */ __name(() => JWKValidator, "JWKValidator"),
193
207
  JWKWithPrivateKeyValidator: /* @__PURE__ */ __name(() => JWKWithPrivateKeyValidator, "JWKWithPrivateKeyValidator"),
208
+ JweCredentialRefreshEnvelopeValidator: /* @__PURE__ */ __name(() => JweCredentialRefreshEnvelopeValidator, "JweCredentialRefreshEnvelopeValidator"),
194
209
  KnownAchievementTypeValidator: /* @__PURE__ */ __name(() => KnownAchievementTypeValidator, "KnownAchievementTypeValidator"),
195
210
  LCNAuthedProfileValidator: /* @__PURE__ */ __name(() => LCNAuthedProfileValidator, "LCNAuthedProfileValidator"),
196
211
  LCNBoostClaimLinkOptionsValidator: /* @__PURE__ */ __name(() => LCNBoostClaimLinkOptionsValidator, "LCNBoostClaimLinkOptionsValidator"),
@@ -228,7 +243,9 @@ var require_types_cjs_development = __commonJS({
228
243
  LCNSigningAuthorityValidator: /* @__PURE__ */ __name(() => LCNSigningAuthorityValidator, "LCNSigningAuthorityValidator"),
229
244
  LCNVisibleProfileValidator: /* @__PURE__ */ __name(() => LCNVisibleProfileValidator, "LCNVisibleProfileValidator"),
230
245
  LaunchTypeValidator: /* @__PURE__ */ __name(() => LaunchTypeValidator, "LaunchTypeValidator"),
246
+ LearnCardRefreshAuthorizationValidator: /* @__PURE__ */ __name(() => LearnCardRefreshAuthorizationValidator, "LearnCardRefreshAuthorizationValidator"),
231
247
  LinkProviderFrameworkInputValidator: /* @__PURE__ */ __name(() => LinkProviderFrameworkInputValidator, "LinkProviderFrameworkInputValidator"),
248
+ ManagedCredentialRefreshServiceValidator: /* @__PURE__ */ __name(() => ManagedCredentialRefreshServiceValidator, "ManagedCredentialRefreshServiceValidator"),
232
249
  PaginatedAppStoreListingsValidator: /* @__PURE__ */ __name(() => PaginatedAppStoreListingsValidator, "PaginatedAppStoreListingsValidator"),
233
250
  PaginatedBoostRecipientsValidator: /* @__PURE__ */ __name(() => PaginatedBoostRecipientsValidator, "PaginatedBoostRecipientsValidator"),
234
251
  PaginatedBoostRecipientsWithChildrenValidator: /* @__PURE__ */ __name(() => PaginatedBoostRecipientsWithChildrenValidator, "PaginatedBoostRecipientsWithChildrenValidator"),
@@ -258,6 +275,12 @@ var require_types_cjs_development = __commonJS({
258
275
  ProfileVisibilityEnum: /* @__PURE__ */ __name(() => ProfileVisibilityEnum, "ProfileVisibilityEnum"),
259
276
  PromotionLevelValidator: /* @__PURE__ */ __name(() => PromotionLevelValidator, "PromotionLevelValidator"),
260
277
  ProofValidator: /* @__PURE__ */ __name(() => ProofValidator, "ProofValidator"),
278
+ PublicCredentialRefreshEnvelopeValidator: /* @__PURE__ */ __name(() => PublicCredentialRefreshEnvelopeValidator, "PublicCredentialRefreshEnvelopeValidator"),
279
+ PublishCredentialRefreshInputValidator: /* @__PURE__ */ __name(() => PublishCredentialRefreshInputValidator, "PublishCredentialRefreshInputValidator"),
280
+ PublishCredentialRefreshNotificationValidator: /* @__PURE__ */ __name(() => PublishCredentialRefreshNotificationValidator, "PublishCredentialRefreshNotificationValidator"),
281
+ PublishCredentialRefreshResultValidator: /* @__PURE__ */ __name(() => PublishCredentialRefreshResultValidator, "PublishCredentialRefreshResultValidator"),
282
+ PublishIssuerSignedRefreshValidator: /* @__PURE__ */ __name(() => PublishIssuerSignedRefreshValidator, "PublishIssuerSignedRefreshValidator"),
283
+ PublishSigningAuthorityRefreshValidator: /* @__PURE__ */ __name(() => PublishSigningAuthorityRefreshValidator, "PublishSigningAuthorityRefreshValidator"),
261
284
  RefreshServiceValidator: /* @__PURE__ */ __name(() => RefreshServiceValidator, "RefreshServiceValidator"),
262
285
  RegExpValidator: /* @__PURE__ */ __name(() => RegExpValidator, "RegExpValidator"),
263
286
  RelatedValidator: /* @__PURE__ */ __name(() => RelatedValidator, "RelatedValidator"),
@@ -298,11 +321,13 @@ var require_types_cjs_development = __commonJS({
298
321
  SkillTreeNodeInputValidator: /* @__PURE__ */ __name(() => SkillTreeNodeInputValidator, "SkillTreeNodeInputValidator"),
299
322
  SkillTreeNodeValidator: /* @__PURE__ */ __name(() => SkillTreeNodeValidator, "SkillTreeNodeValidator"),
300
323
  SkillValidator: /* @__PURE__ */ __name(() => SkillValidator, "SkillValidator"),
324
+ StandardCredentialRefreshServiceValidator: /* @__PURE__ */ __name(() => StandardCredentialRefreshServiceValidator, "StandardCredentialRefreshServiceValidator"),
301
325
  StatusCheckEntryValidator: /* @__PURE__ */ __name(() => StatusCheckEntryValidator, "StatusCheckEntryValidator"),
302
326
  StoredCredentialEnvelopeValidator: /* @__PURE__ */ __name(() => StoredCredentialEnvelopeValidator, "StoredCredentialEnvelopeValidator"),
303
327
  StringQuery: /* @__PURE__ */ __name(() => StringQuery, "StringQuery"),
304
328
  SummaryCredentialDataValidator: /* @__PURE__ */ __name(() => SummaryCredentialDataValidator, "SummaryCredentialDataValidator"),
305
329
  SummaryCredentialKeywordValidator: /* @__PURE__ */ __name(() => SummaryCredentialKeywordValidator, "SummaryCredentialKeywordValidator"),
330
+ SupportedCredentialRefreshServiceValidator: /* @__PURE__ */ __name(() => SupportedCredentialRefreshServiceValidator, "SupportedCredentialRefreshServiceValidator"),
306
331
  SyncFrameworkInputValidator: /* @__PURE__ */ __name(() => SyncFrameworkInputValidator, "SyncFrameworkInputValidator"),
307
332
  TagValidator: /* @__PURE__ */ __name(() => TagValidator, "TagValidator"),
308
333
  TemplateRenderMethodValidator: /* @__PURE__ */ __name(() => TemplateRenderMethodValidator, "TemplateRenderMethodValidator"),
@@ -16506,7 +16531,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16506
16531
  webhookUrl: external_exports.string().url().optional().describe("Webhook URL to receive claim notifications"),
16507
16532
  suppressDelivery: external_exports.boolean().optional().describe("If true, returns claimUrl without sending email/SMS"),
16508
16533
  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")
16534
+ guardianEmail: external_exports.string().email().optional().describe("Guardian email that must approve before student can claim"),
16535
+ expiresInDays: external_exports.number().int().min(1).max(720).optional().describe(
16536
+ "How many days the credential stays claimable in the Universal Inbox (default 30). Does not change the credential validity period."
16537
+ )
16510
16538
  });
16511
16539
  var SendBoostInputValidator = external_exports.object({
16512
16540
  type: external_exports.literal("boost"),
@@ -16801,7 +16829,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16801
16829
  "APP_NOTIFICATION",
16802
16830
  "CREDENTIAL_REVOKED",
16803
16831
  "CREDENTIAL_SUSPENDED",
16804
- "CREDENTIAL_UNSUSPENDED"
16832
+ "CREDENTIAL_UNSUSPENDED",
16833
+ "CREDENTIAL_REFRESHED"
16805
16834
  ]);
16806
16835
  var LCNNotificationMessageValidator = external_exports.object({
16807
16836
  title: external_exports.string().optional(),
@@ -16933,12 +16962,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16933
16962
  });
16934
16963
  var InboxCredentialValidator = external_exports.object({
16935
16964
  id: external_exports.string(),
16936
- credential: external_exports.string(),
16965
+ credential: external_exports.string().optional(),
16937
16966
  isSigned: external_exports.boolean(),
16938
16967
  currentStatus: LCNInboxStatusEnumValidator,
16939
16968
  isAccepted: external_exports.boolean().optional(),
16940
16969
  expiresAt: external_exports.string(),
16941
16970
  createdAt: external_exports.string(),
16971
+ finalizedAt: external_exports.string().optional(),
16972
+ expiredAt: external_exports.string().optional(),
16973
+ credentialName: external_exports.string().optional(),
16974
+ achievementType: external_exports.string().optional(),
16942
16975
  issuerDid: external_exports.string(),
16943
16976
  webhookUrl: external_exports.string().optional(),
16944
16977
  boostUri: external_exports.string().optional(),
@@ -16992,7 +17025,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16992
17025
  "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
17026
  ),
16994
17027
  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."),
17028
+ expiresInDays: external_exports.number().int().min(1).max(720).optional().describe(
17029
+ "How many days the encrypted inbox payload remains claimable. This does not change the credential validity period."
17030
+ ),
16996
17031
  templateData: external_exports.record(external_exports.string(), external_exports.unknown()).optional().describe(
16997
17032
  "Template data to render into the boost credential template using Mustache syntax. Only used when boostUri is provided."
16998
17033
  ),
@@ -17055,6 +17090,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17055
17090
  credential: VCValidator.or(VPValidator).or(UnsignedVCValidator).or(CredentialNameRefValidator).describe("The credential to issue, or a { name } reference to resolve a boost template."),
17056
17091
  configuration: external_exports.object({
17057
17092
  publishableKey: external_exports.string(),
17093
+ expiresInDays: external_exports.number().int().min(1).max(720).optional().describe(
17094
+ "Inbox claim window in days. Defaults to 720; use a shorter window for sensitive records."
17095
+ ),
17058
17096
  signingAuthorityName: external_exports.string().optional(),
17059
17097
  listingId: external_exports.string().optional(),
17060
17098
  listingSlug: external_exports.string().optional()
@@ -17692,6 +17730,191 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17692
17730
  const result = inAppMessagesFlagValidator.safeParse(raw);
17693
17731
  return result.success ? result.data : EMPTY_IN_APP_MESSAGES_FLAG;
17694
17732
  }, "parseInAppMessagesFlag");
17733
+ var LearnCardRefreshAuthorizationValidator = external_exports.object({ type: external_exports.literal("LearnCardDIDAuth") }).catchall(external_exports.any());
17734
+ var ManagedCredentialRefreshServiceValidator = external_exports.object({
17735
+ id: external_exports.string().min(1),
17736
+ type: external_exports.literal("LearnCardCredentialRefresh2026"),
17737
+ authorization: LearnCardRefreshAuthorizationValidator.optional()
17738
+ }).catchall(external_exports.any());
17739
+ var StandardCredentialRefreshServiceValidator = external_exports.object({
17740
+ id: external_exports.string().min(1),
17741
+ type: external_exports.literal("1EdTechCredentialRefresh")
17742
+ }).catchall(external_exports.any());
17743
+ var SupportedCredentialRefreshServiceValidator = external_exports.union([
17744
+ ManagedCredentialRefreshServiceValidator,
17745
+ StandardCredentialRefreshServiceValidator
17746
+ ]);
17747
+ var AllocateCredentialRefreshInputValidator = external_exports.object({
17748
+ holder: external_exports.object({
17749
+ profileId: external_exports.string().optional(),
17750
+ did: external_exports.string().min(1)
17751
+ }),
17752
+ credentialId: external_exports.string().min(1)
17753
+ });
17754
+ var AllocateCredentialRefreshResultValidator = external_exports.object({
17755
+ refreshId: external_exports.string().min(1),
17756
+ refreshService: ManagedCredentialRefreshServiceValidator.extend({
17757
+ authorization: LearnCardRefreshAuthorizationValidator
17758
+ })
17759
+ });
17760
+ var CredentialRefreshSigningModeValidator = external_exports.enum(["issuer-signed", "signing-authority"]);
17761
+ var PublishCredentialRefreshBaseFields = {
17762
+ refreshId: external_exports.string().min(1),
17763
+ notifyHolder: external_exports.boolean().optional(),
17764
+ updateSummary: external_exports.string().optional(),
17765
+ idempotencyKey: external_exports.string().optional()
17766
+ };
17767
+ var PublishIssuerSignedRefreshValidator = external_exports.object({
17768
+ ...PublishCredentialRefreshBaseFields,
17769
+ mode: external_exports.literal("issuer-signed"),
17770
+ signedCredential: VCValidator
17771
+ });
17772
+ var PublishSigningAuthorityRefreshValidator = external_exports.object({
17773
+ ...PublishCredentialRefreshBaseFields,
17774
+ mode: external_exports.literal("signing-authority"),
17775
+ credential: UnsignedVCValidator,
17776
+ signingAuthority: external_exports.object({
17777
+ type: external_exports.string().min(1)
17778
+ }).catchall(external_exports.any())
17779
+ });
17780
+ var PublishCredentialRefreshInputValidator = external_exports.object({
17781
+ ...PublishCredentialRefreshBaseFields,
17782
+ mode: CredentialRefreshSigningModeValidator,
17783
+ signedCredential: VCValidator.optional(),
17784
+ credential: UnsignedVCValidator.optional(),
17785
+ signingAuthority: external_exports.object({
17786
+ type: external_exports.string().min(1)
17787
+ }).catchall(external_exports.any()).optional()
17788
+ }).superRefine((input, ctx) => {
17789
+ if (input.mode === "issuer-signed" && !input.signedCredential) {
17790
+ ctx.addIssue({
17791
+ code: "custom",
17792
+ path: ["signedCredential"],
17793
+ message: "signedCredential is required for issuer-signed publication"
17794
+ });
17795
+ }
17796
+ if (input.mode === "issuer-signed" && (input.credential !== void 0 || input.signingAuthority !== void 0)) {
17797
+ ctx.addIssue({
17798
+ code: "custom",
17799
+ path: ["mode"],
17800
+ message: "issuer-signed publication cannot include signing-authority fields"
17801
+ });
17802
+ }
17803
+ if (input.mode === "signing-authority") {
17804
+ if (input.signedCredential !== void 0) {
17805
+ ctx.addIssue({
17806
+ code: "custom",
17807
+ path: ["signedCredential"],
17808
+ message: "signing-authority publication cannot include signedCredential"
17809
+ });
17810
+ }
17811
+ if (!input.credential) {
17812
+ ctx.addIssue({
17813
+ code: "custom",
17814
+ path: ["credential"],
17815
+ message: "credential is required for signing-authority publication"
17816
+ });
17817
+ }
17818
+ if (!input.signingAuthority) {
17819
+ ctx.addIssue({
17820
+ code: "custom",
17821
+ path: ["signingAuthority"],
17822
+ message: "signingAuthority is required for signing-authority publication"
17823
+ });
17824
+ }
17825
+ }
17826
+ });
17827
+ var PublishCredentialRefreshNotificationValidator = external_exports.enum([
17828
+ "queued",
17829
+ "suppressed",
17830
+ "not-applicable",
17831
+ /** Publication succeeded, but the post-commit notification enqueue must be retried. */
17832
+ "delivery-failed"
17833
+ ]);
17834
+ var PublishCredentialRefreshResultValidator = external_exports.object({
17835
+ refreshId: external_exports.string().min(1),
17836
+ version: external_exports.number().int().positive(),
17837
+ publishedAt: external_exports.string().min(1),
17838
+ notification: PublishCredentialRefreshNotificationValidator
17839
+ });
17840
+ var CredentialRefreshVersionMetadataValidator = external_exports.object({
17841
+ version: external_exports.number().int().positive(),
17842
+ publishedAt: external_exports.string().min(1),
17843
+ effectiveAt: external_exports.string().optional(),
17844
+ etag: external_exports.string().optional(),
17845
+ signingMode: CredentialRefreshSigningModeValidator.optional(),
17846
+ updateSummary: external_exports.string().optional()
17847
+ });
17848
+ var GetCredentialRefreshHistoryInputValidator = external_exports.object({
17849
+ refreshId: external_exports.string().min(1),
17850
+ cursor: external_exports.string().optional(),
17851
+ limit: external_exports.number().int().positive().optional()
17852
+ });
17853
+ var GetCredentialRefreshHistoryResultValidator = external_exports.object({
17854
+ records: CredentialRefreshVersionMetadataValidator.array(),
17855
+ hasMore: external_exports.boolean(),
17856
+ cursor: external_exports.string().optional()
17857
+ });
17858
+ var CredentialRefreshChallengeValidator = external_exports.object({
17859
+ challenge: external_exports.string().min(1),
17860
+ expiresAt: external_exports.string().min(1),
17861
+ domain: external_exports.string().optional(),
17862
+ scheme: external_exports.literal("LearnCardDIDAuth").optional()
17863
+ });
17864
+ var PublicCredentialRefreshEnvelopeValidator = external_exports.object({
17865
+ format: external_exports.literal("vc"),
17866
+ credential: VCValidator,
17867
+ etag: external_exports.string().optional()
17868
+ });
17869
+ var JweCredentialRefreshEnvelopeValidator = external_exports.object({
17870
+ format: external_exports.literal("jwe"),
17871
+ jwe: JWEValidator,
17872
+ etag: external_exports.string().optional(),
17873
+ /** Present on LearnCard-managed endpoints; optional for interoperable JWE services. */
17874
+ version: external_exports.number().int().positive().optional()
17875
+ });
17876
+ var CredentialRefreshResponseEnvelopeValidator = external_exports.discriminatedUnion("format", [
17877
+ PublicCredentialRefreshEnvelopeValidator,
17878
+ JweCredentialRefreshEnvelopeValidator
17879
+ ]);
17880
+ var CredentialRefreshFailureCodeValidator = external_exports.enum([
17881
+ "UNAVAILABLE",
17882
+ "TIMEOUT",
17883
+ "UNSUPPORTED_SERVICE",
17884
+ "UNAUTHORIZED",
17885
+ "MALFORMED_RESPONSE",
17886
+ "INVALID_PROOF",
17887
+ "ISSUER_MISMATCH",
17888
+ "ID_MISMATCH",
17889
+ "ROLLBACK",
17890
+ "REVOKED",
17891
+ "UNSAFE_ENDPOINT"
17892
+ ]);
17893
+ var CredentialRefreshUpdatedResultValidator = external_exports.object({
17894
+ status: external_exports.literal("updated"),
17895
+ credential: VCValidator,
17896
+ etag: external_exports.string().optional(),
17897
+ managedVersion: external_exports.number().int().positive().optional()
17898
+ });
17899
+ var CredentialRefreshUnchangedResultValidator = external_exports.object({
17900
+ status: external_exports.literal("unchanged"),
17901
+ checkedAt: external_exports.string().min(1),
17902
+ etag: external_exports.string().optional()
17903
+ });
17904
+ var CredentialRefreshUnsupportedResultValidator = external_exports.object({
17905
+ status: external_exports.literal("unsupported")
17906
+ });
17907
+ var CredentialRefreshFailedResultValidator = external_exports.object({
17908
+ status: external_exports.literal("failed"),
17909
+ code: CredentialRefreshFailureCodeValidator,
17910
+ retryable: external_exports.boolean()
17911
+ });
17912
+ var CredentialRefreshResultValidator = external_exports.discriminatedUnion("status", [
17913
+ CredentialRefreshUpdatedResultValidator,
17914
+ CredentialRefreshUnchangedResultValidator,
17915
+ CredentialRefreshUnsupportedResultValidator,
17916
+ CredentialRefreshFailedResultValidator
17917
+ ]);
17695
17918
  }
17696
17919
  });
17697
17920