Skip to main content
tauri-plugin-configurate returns dedicated wrapper classes rather than raw objects so that keyring state — locked versus unlocked — is visible in the type system. When a method returns LockedConfig<S>, TypeScript tells you that keyring fields are null; when it returns UnlockedConfig<S>, you know the full data is available. The types described on this page are the building blocks of that contract.

LockedConfig<S>

The result of loading or writing a config without unlocking keyring fields. Keyring-protected fields in data are typed as null and will always be null at runtime — they were never fetched from the OS keyring.
class LockedConfig<S> {
  readonly data: InferLocked<S>;
  unlock(opts: KeyringOptions): Promise<UnlockedConfig<S>>;
}
data
InferLocked<S>
The config data with every keyring() field replaced by null. All other fields have their normal types.
unlock(opts)
Promise<UnlockedConfig<S>>
Fetches the keyring-protected values from the OS credential store and returns a new UnlockedConfig<S> with those fields populated.
const locked = await config.load().run();

// Keyring fields are null here
console.log(locked.data.apiKey); // null

// Fetch secrets and get the full data
const unlocked = await locked.unlock({ service: "my-app", account: "default" });
console.log(unlocked.data.apiKey); // "secret"

UnlockedConfig<S>

Holds the full config data with keyring fields populated. Access to data is revoked when you call .lock(), which sets the internal reference to null. This is an API-level access guard — it signals that the calling code is done with the sensitive values; it does not zero-clear memory.
class UnlockedConfig<S> {
  get data(): InferUnlocked<S>;  // throws after lock()
  lock(): void;                  // revokes access to data
}
data
InferUnlocked<S>
The complete config with all keyring fields resolved to their actual types. Accessing this property after calling lock() throws "Cannot access data after lock() has been called. Load or unlock again.".
lock()
void
Sets the internal data reference to null. Subsequent reads of data throw an error. Call this when you no longer need the sensitive values within the current scope.
const unlocked = await config.load().unlock({
  service: "my-app",
  account: "default",
});

const token = unlocked.data.apiKey; // "secret"

// Signal that we're done with the sensitive data
unlocked.lock();

unlocked.data; // throws: "Cannot access data after lock() has been called. Load or unlock again."
lock() is an API-level access guard, not a cryptographic memory wipe. JavaScript’s garbage collector manages memory reclamation and immediate clearing cannot be guaranteed. Do not rely on lock() for security-critical wiping.

PatchedConfig<S>

The result of a patch().run() call. Contains only the partial data that was written — not the full merged config — so the shape is Partial<InferLocked<S>>.
class PatchedConfig<S> {
  readonly data: Partial<InferLocked<S>>;
}
data
Partial<InferLocked<S>>
The subset of fields that were included in the patch payload, with keyring fields typed as null. This is not the full merged config; call load() separately if you need the complete state after patching.
const patched = await config.patch({ theme: "dark" }).run();
console.log(patched.data.theme); // "dark"
// patched.data.fontSize is undefined — it was not part of the patch

LazyConfigEntry<S>

The chainable builder returned by create(), load(), and save(). Nothing is sent over IPC until you call .run() or .unlock().
class LazyConfigEntry<S> {
  lock(opts: KeyringOptions): this;
  run(): Promise<LockedConfig<S>>;
  unlock(opts: KeyringOptions): Promise<UnlockedConfig<S>>;
}
.lock(opts)
this
Stores the provided KeyringOptions on the builder. Call this before .run() when your schema contains keyring() fields, so the plugin knows where to store secrets in the OS keyring.
.run()
Promise<LockedConfig<S>>
Executes the operation and returns a LockedConfig<S>. If the schema has keyring fields and you did not call .lock() first, the operation will throw.
.unlock(opts)
Promise<UnlockedConfig<S>>
Executes the operation, stores keyring secrets, and returns an UnlockedConfig<S> in one step.
// Minimal — no keyring fields
const locked = await config.create({ theme: "dark" }).run();

// With keyring fields — must lock first
const locked = await config
  .create({ theme: "dark", apiKey: "secret" })
  .lock({ service: "my-app", account: "default" })
  .run();

// Get unlocked result in one call
const unlocked = await config
  .create({ theme: "dark", apiKey: "secret" })
  .unlock({ service: "my-app", account: "default" });

LazyPatchEntry<S>

The chainable builder returned by patch(). Adds a .createIfMissing() modifier not present on LazyConfigEntry.
class LazyPatchEntry<S> {
  lock(opts: KeyringOptions): this;
  createIfMissing(): this;
  run(): Promise<PatchedConfig<S>>;
  unlock(opts: KeyringOptions): Promise<UnlockedConfig<S>>;
}
.lock(opts)
this
Attaches KeyringOptions to the builder, required when patching keyring fields.
.createIfMissing()
this
When chained, the patch creates the config from the partial data if the config file does not yet exist, instead of throwing a not-found error.
.run()
Promise<PatchedConfig<S>>
Executes the patch and returns the patched subset as a PatchedConfig<S>.
.unlock(opts)
Promise<UnlockedConfig<S>>
Executes the patch with keyring support and returns a fully unlocked UnlockedConfig<S>.
// Basic patch
const patched = await config.patch({ theme: "light" }).run();

// Patch with keyring
const patched = await config
  .patch({ apiKey: "rotated" })
  .lock({ service: "my-app", account: "default" })
  .run();

// Upsert-style patch
const patched = await config
  .patch({ theme: "dark" })
  .createIfMissing()
  .run();

BatchRunResult

Returned by all three static batch methods (Configurate.loadAll, Configurate.saveAll, Configurate.patchAll). Each entry in results is indexed by the id you assigned in the input array and holds either a success value or a structured error.
interface BatchRunResult {
  results: Record<string, BatchRunEntryResult>;
}

type BatchRunEntryResult =
  | { ok: true; data: unknown }
  | { ok: false; error: { kind: string; message: string } };
results
Record<string, BatchRunEntryResult>
A map of id → result. Always check result.ok before accessing result.data; if ok is false, inspect result.error.kind and result.error.message for diagnostics.
const result = await Configurate.loadAll([
  { id: "app", config: appConfig },
  { id: "user", config: userConfig },
]).run();

for (const [id, entry] of Object.entries(result.results)) {
  if (entry.ok) {
    console.log(id, entry.data);
  } else {
    console.error(id, entry.error.kind, entry.error.message);
  }
}

KeyringOptions

Passed to .lock(), .unlock(), delete(), exportAs(), importFrom(), and LockedConfig.unlock() whenever a keyring operation is needed.
interface KeyringOptions {
  service: string; // Keyring service name
  account: string; // Keyring account name
}
service
string
The service name registered in the OS credential store. Typically your application name. Must not be empty and must not contain control characters.
account
string
The account name within the service. Typically "default" or a user identifier. Must not be empty and must not contain control characters.
const keyringOpts: KeyringOptions = {
  service: "com.example.my-app",
  account: "default",
};

ConfigChangeEvent

The event payload delivered to callbacks registered with onChange() and watchExternal().
interface ConfigChangeEvent {
  fileName: string;
  operation: string;
  targetId: string;
}
fileName
string
The fileName of the config instance that changed.
operation
string
A string describing what happened. One of: "create", "save", "patch", "delete", "reset", "import", or "external_change". The "external_change" value is only emitted by watchExternal().
targetId
string
An opaque identifier that uniquely identifies the config target. You do not need to construct or compare this value directly; it is provided so you can distinguish which config instance triggered the event when multiple instances share a listener.
const unlisten = await config.onChange((event: ConfigChangeEvent) => {
  console.log(`${event.operation} on ${event.fileName}`);
  // "save on app.json"
});