@objectstack/service-settings 7.4.1 → 7.6.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.
package/dist/index.d.cts CHANGED
@@ -332,12 +332,36 @@ declare class SettingsService {
332
332
  private materialiseRow;
333
333
  }
334
334
 
335
- declare class InMemoryCryptoProvider implements ICryptoProvider {
335
+ type EnvMap = Record<string, string | undefined>;
336
+ /** Where the provider resolved its data key from (for diagnostics). */
337
+ type KeySource = 'explicit' | 'env:OS_SECRET_KEY' | 'env:OS_DEV_CRYPTO_KEY' | 'file' | 'generated-file' | 'ephemeral';
338
+ type CryptoMode = 'production' | 'development' | 'test';
339
+ interface LocalCryptoProviderOptions {
340
+ /** Explicit 32-byte data key. Overrides all env / file resolution. */
341
+ key?: Buffer;
342
+ /**
343
+ * Env source. Defaults to `process.env`. Injectable so embedders and
344
+ * tests can drive key resolution deterministically.
345
+ */
346
+ env?: EnvMap;
347
+ /**
348
+ * Deployment mode. Controls whether an ephemeral / auto-generated key is
349
+ * tolerated. Defaults to auto-detection from `NODE_ENV`:
350
+ * - `production` → a stable key (env var or pre-existing file) is
351
+ * REQUIRED; construction throws otherwise (fail loud).
352
+ * - `development` → persists an auto-generated key to disk so restarts
353
+ * reuse it; falls back to an ephemeral key (loud warning) if disk is
354
+ * unwritable.
355
+ * - `test` → never touches disk; uses an ephemeral key silently.
356
+ */
357
+ mode?: CryptoMode;
358
+ }
359
+ declare class LocalCryptoProvider implements ICryptoProvider {
336
360
  private readonly key;
337
361
  private readonly useNoble;
338
- constructor(opts?: {
339
- key?: Buffer;
340
- });
362
+ /** Where the active data key came from. Exposed for diagnostics/tests. */
363
+ readonly keySource: KeySource;
364
+ constructor(opts?: LocalCryptoProviderOptions);
341
365
  encrypt(plain: string, ctx: CryptoContext): Promise<CryptoHandle>;
342
366
  decrypt(handle: CryptoHandle, ctx: CryptoContext): Promise<string>;
343
367
  rotateKey(handle: CryptoHandle, ctx: CryptoContext): Promise<CryptoHandle>;
@@ -345,6 +369,12 @@ declare class InMemoryCryptoProvider implements ICryptoProvider {
345
369
  private encryptNode;
346
370
  private aadOf;
347
371
  }
372
+ /**
373
+ * @deprecated Renamed to {@link LocalCryptoProvider}. The old name implied an
374
+ * "in-memory / ephemeral" key when the provider in fact persists its key
375
+ * (env var or on-disk file). Kept as an alias for backward compatibility.
376
+ */
377
+ declare const InMemoryCryptoProvider: typeof LocalCryptoProvider;
348
378
 
349
379
  /** Configuration options for the SettingsServicePlugin. */
350
380
  interface SettingsServicePluginOptions {
@@ -360,9 +390,11 @@ interface SettingsServicePluginOptions {
360
390
  /**
361
391
  * Phase 3 KMS hook. When provided, encrypted specifier values are
362
392
  * routed through this provider into `sys_secret`; `sys_setting.value_enc`
363
- * holds the handle id only. Defaults to `InMemoryCryptoProvider`
364
- * (NOT suitable for production secrets replace with an AWS / GCP
365
- * KMS-backed implementation).
393
+ * holds the handle id only. Defaults to `LocalCryptoProvider`, an
394
+ * AES-256-GCM provider keyed off `OS_SECRET_KEY` (or a persisted dev key).
395
+ * In production it refuses to boot without a stable key rather than
396
+ * silently minting an ephemeral one. Swap in an AWS / GCP / Vault
397
+ * KMS-backed implementation for managed custody and per-tenant keys.
366
398
  */
367
399
  cryptoProvider?: ICryptoProvider;
368
400
  /** Override the default base path (`/api/settings`). */
@@ -612,4 +644,4 @@ declare const jaJP: TranslationData;
612
644
 
613
645
  declare const settingsBuiltinTranslations: TranslationBundle;
614
646
 
615
- export { type CryptoAdapter, InMemoryCryptoProvider, NoopCryptoAdapter, SETTINGS_PLUGIN_ID, SETTINGS_PLUGIN_VERSION, type SettingsActionHandler, type SettingsAuditSink, type SettingsContext, type SettingsEngine, SettingsLockedError, type SettingsRoutesOptions, type SettingsRow, SettingsService, type SettingsServiceOptions, SettingsServicePlugin, type SettingsServicePluginOptions, UnknownKeyError, UnknownNamespaceError, brandingSettingsManifest, builtinSettingsManifests, envKeyOf, featureFlagsSettingsManifest, mailSettingsManifest, mailTestActionHandler, registerSettingsRoutes, settingsBuiltinTranslations, settingsObjects, settingsPluginManifestHeader, en as settingsTranslationsEn, jaJP as settingsTranslationsJaJP, zhCN as settingsTranslationsZhCN, storageSettingsManifest, storageTestActionHandler };
647
+ export { type CryptoAdapter, type CryptoMode, InMemoryCryptoProvider, type KeySource, LocalCryptoProvider, type LocalCryptoProviderOptions, NoopCryptoAdapter, SETTINGS_PLUGIN_ID, SETTINGS_PLUGIN_VERSION, type SettingsActionHandler, type SettingsAuditSink, type SettingsContext, type SettingsEngine, SettingsLockedError, type SettingsRoutesOptions, type SettingsRow, SettingsService, type SettingsServiceOptions, SettingsServicePlugin, type SettingsServicePluginOptions, UnknownKeyError, UnknownNamespaceError, brandingSettingsManifest, builtinSettingsManifests, envKeyOf, featureFlagsSettingsManifest, mailSettingsManifest, mailTestActionHandler, registerSettingsRoutes, settingsBuiltinTranslations, settingsObjects, settingsPluginManifestHeader, en as settingsTranslationsEn, jaJP as settingsTranslationsJaJP, zhCN as settingsTranslationsZhCN, storageSettingsManifest, storageTestActionHandler };
package/dist/index.d.ts CHANGED
@@ -332,12 +332,36 @@ declare class SettingsService {
332
332
  private materialiseRow;
333
333
  }
334
334
 
335
- declare class InMemoryCryptoProvider implements ICryptoProvider {
335
+ type EnvMap = Record<string, string | undefined>;
336
+ /** Where the provider resolved its data key from (for diagnostics). */
337
+ type KeySource = 'explicit' | 'env:OS_SECRET_KEY' | 'env:OS_DEV_CRYPTO_KEY' | 'file' | 'generated-file' | 'ephemeral';
338
+ type CryptoMode = 'production' | 'development' | 'test';
339
+ interface LocalCryptoProviderOptions {
340
+ /** Explicit 32-byte data key. Overrides all env / file resolution. */
341
+ key?: Buffer;
342
+ /**
343
+ * Env source. Defaults to `process.env`. Injectable so embedders and
344
+ * tests can drive key resolution deterministically.
345
+ */
346
+ env?: EnvMap;
347
+ /**
348
+ * Deployment mode. Controls whether an ephemeral / auto-generated key is
349
+ * tolerated. Defaults to auto-detection from `NODE_ENV`:
350
+ * - `production` → a stable key (env var or pre-existing file) is
351
+ * REQUIRED; construction throws otherwise (fail loud).
352
+ * - `development` → persists an auto-generated key to disk so restarts
353
+ * reuse it; falls back to an ephemeral key (loud warning) if disk is
354
+ * unwritable.
355
+ * - `test` → never touches disk; uses an ephemeral key silently.
356
+ */
357
+ mode?: CryptoMode;
358
+ }
359
+ declare class LocalCryptoProvider implements ICryptoProvider {
336
360
  private readonly key;
337
361
  private readonly useNoble;
338
- constructor(opts?: {
339
- key?: Buffer;
340
- });
362
+ /** Where the active data key came from. Exposed for diagnostics/tests. */
363
+ readonly keySource: KeySource;
364
+ constructor(opts?: LocalCryptoProviderOptions);
341
365
  encrypt(plain: string, ctx: CryptoContext): Promise<CryptoHandle>;
342
366
  decrypt(handle: CryptoHandle, ctx: CryptoContext): Promise<string>;
343
367
  rotateKey(handle: CryptoHandle, ctx: CryptoContext): Promise<CryptoHandle>;
@@ -345,6 +369,12 @@ declare class InMemoryCryptoProvider implements ICryptoProvider {
345
369
  private encryptNode;
346
370
  private aadOf;
347
371
  }
372
+ /**
373
+ * @deprecated Renamed to {@link LocalCryptoProvider}. The old name implied an
374
+ * "in-memory / ephemeral" key when the provider in fact persists its key
375
+ * (env var or on-disk file). Kept as an alias for backward compatibility.
376
+ */
377
+ declare const InMemoryCryptoProvider: typeof LocalCryptoProvider;
348
378
 
349
379
  /** Configuration options for the SettingsServicePlugin. */
350
380
  interface SettingsServicePluginOptions {
@@ -360,9 +390,11 @@ interface SettingsServicePluginOptions {
360
390
  /**
361
391
  * Phase 3 KMS hook. When provided, encrypted specifier values are
362
392
  * routed through this provider into `sys_secret`; `sys_setting.value_enc`
363
- * holds the handle id only. Defaults to `InMemoryCryptoProvider`
364
- * (NOT suitable for production secrets replace with an AWS / GCP
365
- * KMS-backed implementation).
393
+ * holds the handle id only. Defaults to `LocalCryptoProvider`, an
394
+ * AES-256-GCM provider keyed off `OS_SECRET_KEY` (or a persisted dev key).
395
+ * In production it refuses to boot without a stable key rather than
396
+ * silently minting an ephemeral one. Swap in an AWS / GCP / Vault
397
+ * KMS-backed implementation for managed custody and per-tenant keys.
366
398
  */
367
399
  cryptoProvider?: ICryptoProvider;
368
400
  /** Override the default base path (`/api/settings`). */
@@ -612,4 +644,4 @@ declare const jaJP: TranslationData;
612
644
 
613
645
  declare const settingsBuiltinTranslations: TranslationBundle;
614
646
 
615
- export { type CryptoAdapter, InMemoryCryptoProvider, NoopCryptoAdapter, SETTINGS_PLUGIN_ID, SETTINGS_PLUGIN_VERSION, type SettingsActionHandler, type SettingsAuditSink, type SettingsContext, type SettingsEngine, SettingsLockedError, type SettingsRoutesOptions, type SettingsRow, SettingsService, type SettingsServiceOptions, SettingsServicePlugin, type SettingsServicePluginOptions, UnknownKeyError, UnknownNamespaceError, brandingSettingsManifest, builtinSettingsManifests, envKeyOf, featureFlagsSettingsManifest, mailSettingsManifest, mailTestActionHandler, registerSettingsRoutes, settingsBuiltinTranslations, settingsObjects, settingsPluginManifestHeader, en as settingsTranslationsEn, jaJP as settingsTranslationsJaJP, zhCN as settingsTranslationsZhCN, storageSettingsManifest, storageTestActionHandler };
647
+ export { type CryptoAdapter, type CryptoMode, InMemoryCryptoProvider, type KeySource, LocalCryptoProvider, type LocalCryptoProviderOptions, NoopCryptoAdapter, SETTINGS_PLUGIN_ID, SETTINGS_PLUGIN_VERSION, type SettingsActionHandler, type SettingsAuditSink, type SettingsContext, type SettingsEngine, SettingsLockedError, type SettingsRoutesOptions, type SettingsRow, SettingsService, type SettingsServiceOptions, SettingsServicePlugin, type SettingsServicePluginOptions, UnknownKeyError, UnknownNamespaceError, brandingSettingsManifest, builtinSettingsManifests, envKeyOf, featureFlagsSettingsManifest, mailSettingsManifest, mailTestActionHandler, registerSettingsRoutes, settingsBuiltinTranslations, settingsObjects, settingsPluginManifestHeader, en as settingsTranslationsEn, jaJP as settingsTranslationsJaJP, zhCN as settingsTranslationsZhCN, storageSettingsManifest, storageTestActionHandler };
package/dist/index.js CHANGED
@@ -572,36 +572,25 @@ function coerceEnvValue(raw, hint) {
572
572
  return raw;
573
573
  }
574
574
 
575
- // src/in-memory-crypto-provider.ts
576
- import { readEnvWithDeprecation } from "@objectstack/types";
575
+ // src/local-crypto-provider.ts
577
576
  import { createHash, randomBytes, createCipheriv, createDecipheriv } from "crypto";
578
577
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
579
578
  import { homedir } from "os";
580
579
  import { dirname, join } from "path";
580
+ var SECRET_KEY_ENV = "OS_SECRET_KEY";
581
581
  var DEV_KEY_ENV = "OS_DEV_CRYPTO_KEY";
582
582
  var DEV_KEY_LEGACY_ENV = "OBJECTSTACK_DEV_CRYPTO_KEY";
583
- var devKeyFallbackPath = () => {
584
- const proc = globalThis?.process;
585
- const home = readEnvWithDeprecation("OS_HOME", "OBJECTSTACK_HOME") || (proc?.env?.HOME ? join(proc.env.HOME, ".objectstack") : void 0) || join(homedir(), ".objectstack");
586
- return join(home, "dev-crypto-key");
583
+ var processEnv = () => globalThis.process?.env ?? {};
584
+ var detectMode = (env) => {
585
+ if (env.VITEST || env.NODE_ENV === "test") return "test";
586
+ if (env.NODE_ENV === "production") return "production";
587
+ return "development";
587
588
  };
588
- var loadOrCreateDevKey = () => {
589
- try {
590
- const path = devKeyFallbackPath();
591
- if (existsSync(path)) {
592
- const raw = readFileSync(path, "utf8").trim();
593
- const parsed = parseDevKey(raw);
594
- if (parsed) return { key: parsed, path, generated: false };
595
- }
596
- const key = randomBytes(32);
597
- mkdirSync(dirname(path), { recursive: true });
598
- writeFileSync(path, key.toString("base64"), { mode: 384 });
599
- return { key, path, generated: true };
600
- } catch {
601
- return void 0;
602
- }
589
+ var keyFilePath = (env) => {
590
+ const home = env.OS_HOME || env.OBJECTSTACK_HOME || (env.HOME ? join(env.HOME, ".objectstack") : void 0) || join(homedir(), ".objectstack");
591
+ return join(home, "dev-crypto-key");
603
592
  };
604
- var parseDevKey = (raw) => {
593
+ var parseKey = (raw) => {
605
594
  if (!raw) return void 0;
606
595
  const trimmed = raw.trim();
607
596
  if (!trimmed) return void 0;
@@ -612,11 +601,99 @@ var parseDevKey = (raw) => {
612
601
  if (buf.length === 32) return buf;
613
602
  } catch {
614
603
  }
615
- console.warn(
616
- `[InMemoryCryptoProvider] ${DEV_KEY_ENV} is set but is not 32 bytes (hex or base64). Ignoring and generating an ephemeral key.`
617
- );
618
604
  return void 0;
619
605
  };
606
+ var loadExistingKey = (path) => {
607
+ try {
608
+ if (!existsSync(path)) return void 0;
609
+ return parseKey(readFileSync(path, "utf8").trim());
610
+ } catch {
611
+ return void 0;
612
+ }
613
+ };
614
+ var loadOrCreateKey = (path) => {
615
+ try {
616
+ const existing = loadExistingKey(path);
617
+ if (existing) return { key: existing, generated: false };
618
+ const key = randomBytes(32);
619
+ mkdirSync(dirname(path), { recursive: true });
620
+ writeFileSync(path, key.toString("base64"), { mode: 384 });
621
+ return { key, generated: true };
622
+ } catch {
623
+ return void 0;
624
+ }
625
+ };
626
+ var INVALID_KEY_MSG = (name) => `[LocalCryptoProvider] ${name} is set but is not a 32-byte key (expected 64 hex chars or base64 of 32 bytes). Generate one with \`openssl rand -hex 32\`.`;
627
+ var MISSING_PROD_KEY_MSG = (path) => `[LocalCryptoProvider] Refusing to start in production without a stable encryption key.
628
+ No ${SECRET_KEY_ENV} (or ${DEV_KEY_ENV}) is set and no persisted key file was found at:
629
+ ${path}
630
+ Minting a key here would make every sys_secret value (encrypted settings, secret
631
+ fields, datasource credentials) undecryptable after the next restart or on another node.
632
+ Fix: generate a 32-byte key and set it in the environment (identical across every
633
+ restart and every node), e.g.
634
+ ${SECRET_KEY_ENV}=$(openssl rand -hex 32)`;
635
+ var warn = (msg) => {
636
+ try {
637
+ globalThis.console?.warn?.(msg);
638
+ } catch {
639
+ }
640
+ };
641
+ var legacyDeprecationWarned = { value: false };
642
+ function resolveDataKey(opts) {
643
+ if (opts.key) return { key: opts.key, source: "explicit" };
644
+ const env = opts.env ?? processEnv();
645
+ const mode = opts.mode ?? detectMode(env);
646
+ if (env[SECRET_KEY_ENV] !== void 0) {
647
+ const parsed = parseKey(env[SECRET_KEY_ENV]);
648
+ if (parsed) return { key: parsed, source: "env:OS_SECRET_KEY" };
649
+ throw new Error(INVALID_KEY_MSG(SECRET_KEY_ENV));
650
+ }
651
+ let devRaw = env[DEV_KEY_ENV];
652
+ if (devRaw === void 0 && env[DEV_KEY_LEGACY_ENV] !== void 0) {
653
+ devRaw = env[DEV_KEY_LEGACY_ENV];
654
+ if (!legacyDeprecationWarned.value) {
655
+ legacyDeprecationWarned.value = true;
656
+ warn(
657
+ `[ObjectStack] Env var \`${DEV_KEY_LEGACY_ENV}\` is deprecated; rename it to \`${DEV_KEY_ENV}\`.`
658
+ );
659
+ }
660
+ }
661
+ if (devRaw !== void 0) {
662
+ const parsed = parseKey(devRaw);
663
+ if (parsed) return { key: parsed, source: "env:OS_DEV_CRYPTO_KEY" };
664
+ if (mode === "production") throw new Error(INVALID_KEY_MSG(DEV_KEY_ENV));
665
+ warn(`${INVALID_KEY_MSG(DEV_KEY_ENV)} Ignoring and generating a local key.`);
666
+ }
667
+ if (mode === "test") {
668
+ return { key: randomBytes(32), source: "ephemeral" };
669
+ }
670
+ const path = keyFilePath(env);
671
+ if (mode === "production") {
672
+ const existing = loadExistingKey(path);
673
+ if (existing) {
674
+ warn(
675
+ `[LocalCryptoProvider] No ${SECRET_KEY_ENV} set \u2014 using the persisted key at ${path}. For containers / multi-node, prefer setting ${SECRET_KEY_ENV} so every node shares one key.`
676
+ );
677
+ return { key: existing, source: "file" };
678
+ }
679
+ throw new Error(MISSING_PROD_KEY_MSG(path));
680
+ }
681
+ const persisted = loadOrCreateKey(path);
682
+ if (persisted) {
683
+ if (persisted.generated) {
684
+ warn(
685
+ `[LocalCryptoProvider] No ${SECRET_KEY_ENV}/${DEV_KEY_ENV} set \u2014 generated a new AES-256-GCM key and persisted it to ${path} (mode 0600). Restarts on this host reuse it automatically. For containers, CI, or multi-node, set ${SECRET_KEY_ENV} explicitly so the key survives.`
686
+ );
687
+ }
688
+ return { key: persisted.key, source: persisted.generated ? "generated-file" : "file" };
689
+ }
690
+ const key = randomBytes(32);
691
+ warn(
692
+ `[LocalCryptoProvider] No ${SECRET_KEY_ENV} set and could not persist a fallback key at ${path} \u2014 generated an EPHEMERAL key. Existing encrypted settings/secrets will fail to decrypt after restart. Set ${SECRET_KEY_ENV} to a stable 32-byte key:
693
+ ${SECRET_KEY_ENV}=${key.toString("base64")}`
694
+ );
695
+ return { key, source: "ephemeral" };
696
+ }
620
697
  var isWebContainerRuntime = () => {
621
698
  const g = globalThis;
622
699
  return typeof g !== "undefined" && (Boolean(g.process?.versions?.webcontainer) || Boolean(g.process?.env?.SHELL?.includes?.("jsh")) || Boolean(g.process?.env?.STACKBLITZ));
@@ -629,8 +706,8 @@ var loadNobleGcm = () => {
629
706
  const mod = await import("@noble/ciphers/aes.js");
630
707
  return mod.gcm;
631
708
  } catch (err) {
632
- console.warn(
633
- `[InMemoryCryptoProvider] WebContainer detected but @noble/ciphers not installed: ${err?.message ?? err}. Falling back to node:crypto (will throw).`
709
+ warn(
710
+ `[LocalCryptoProvider] WebContainer detected but @noble/ciphers not installed: ${err?.message ?? err}. Falling back to node:crypto (will throw).`
634
711
  );
635
712
  return void 0;
636
713
  }
@@ -638,39 +715,11 @@ var loadNobleGcm = () => {
638
715
  }
639
716
  return nobleGcmPromise;
640
717
  };
641
- var InMemoryCryptoProvider = class {
718
+ var LocalCryptoProvider = class {
642
719
  constructor(opts = {}) {
643
- if (opts.key) {
644
- this.key = opts.key;
645
- } else {
646
- const fromEnv = parseDevKey(
647
- readEnvWithDeprecation(DEV_KEY_ENV, DEV_KEY_LEGACY_ENV)
648
- );
649
- if (fromEnv) {
650
- this.key = fromEnv;
651
- } else {
652
- const isTest = Boolean(
653
- globalThis?.process?.env?.VITEST || globalThis?.process?.env?.NODE_ENV === "test"
654
- );
655
- const persisted = isTest ? void 0 : loadOrCreateDevKey();
656
- if (persisted) {
657
- this.key = persisted.key;
658
- if (persisted.generated) {
659
- console.warn(
660
- `[InMemoryCryptoProvider] No ${DEV_KEY_ENV} set \u2014 generated a new AES-256-GCM key and persisted it to ${persisted.path} (mode 0600). Future restarts will reuse it automatically. For shared/CI environments, set ${DEV_KEY_ENV} explicitly in your environment.`
661
- );
662
- }
663
- } else {
664
- this.key = randomBytes(32);
665
- if (!isTest) {
666
- console.warn(
667
- `[InMemoryCryptoProvider] No ${DEV_KEY_ENV} set and could not persist a fallback key \u2014 generated an ephemeral AES-256-GCM key. Existing encrypted settings (e.g. AI API keys) will fail to decrypt on next restart. To make the key survive restarts, add this to your .env:
668
- ${DEV_KEY_ENV}=${this.key.toString("base64")}`
669
- );
670
- }
671
- }
672
- }
673
- }
720
+ const resolved = resolveDataKey(opts);
721
+ this.key = resolved.key;
722
+ this.keySource = resolved.source;
674
723
  this.useNoble = isWebContainerRuntime();
675
724
  }
676
725
  async encrypt(plain, ctx) {
@@ -694,7 +743,7 @@ var InMemoryCryptoProvider = class {
694
743
  }
695
744
  return {
696
745
  id: "sec_" + randomBytes(16).toString("hex"),
697
- kmsKeyId: "local:in-memory:v1",
746
+ kmsKeyId: "local:v1",
698
747
  alg: "aes-256-gcm",
699
748
  version: 1,
700
749
  ciphertext: blob
@@ -726,7 +775,7 @@ var InMemoryCryptoProvider = class {
726
775
  return {
727
776
  ...next,
728
777
  id: handle.id,
729
- kmsKeyId: `local:in-memory:v${handle.version + 1}`,
778
+ kmsKeyId: `local:v${handle.version + 1}`,
730
779
  version: handle.version + 1
731
780
  };
732
781
  }
@@ -744,6 +793,7 @@ var InMemoryCryptoProvider = class {
744
793
  return [ctx.namespace, ctx.key].join("|");
745
794
  }
746
795
  };
796
+ var InMemoryCryptoProvider = LocalCryptoProvider;
747
797
 
748
798
  // src/settings-routes.ts
749
799
  var defaultContext = (req) => {
@@ -3023,7 +3073,7 @@ var SettingsServicePlugin = class {
3023
3073
  {
3024
3074
  secretStore: this.buildSecretStore(engine),
3025
3075
  auditWriter: this.buildAuditWriter(ctx, engine),
3026
- cryptoProvider: this.opts.cryptoProvider ?? new InMemoryCryptoProvider()
3076
+ cryptoProvider: this.opts.cryptoProvider ?? new LocalCryptoProvider()
3027
3077
  }
3028
3078
  );
3029
3079
  }
@@ -3159,6 +3209,7 @@ function wrapEngineAsSettingsEngine(engine) {
3159
3209
  }
3160
3210
  export {
3161
3211
  InMemoryCryptoProvider,
3212
+ LocalCryptoProvider,
3162
3213
  NoopCryptoAdapter,
3163
3214
  SETTINGS_PLUGIN_ID,
3164
3215
  SETTINGS_PLUGIN_VERSION,