tauri-plugin-configurate 0.3.0

Tauri v2 plugin for type-safe application configuration management.
Documentation

tauri-plugin-configurate

Tauri v2 plugin for type-safe application configuration management with OS keyring support.

Store app settings as JSON, YAML, Binary, or SQLite — with sensitive values automatically secured in the OS keyring (Windows Credential Manager / macOS Keychain / Linux Secret Service).


[!WARNING] Pre-release software (0.x)

This plugin is under active development and has not reached a stable 1.0 release.

  • Breaking changes may be introduced in any minor version (e.g. 0.2 → 0.3).
  • Bugs may be present. Please report issues on GitHub.
  • The on-disk format of BinaryProvider() (unencrypted) changed in v0.2.3 — existing files written by v0.2.2 or earlier must be re-created. Encrypted binary files (BinaryProvider({ encryptionKey })) are not affected.

Pin to an exact version in production and review the release notes before upgrading.


Features

Feature Description
🛡️ Type-safe schema Define your config shape with defineConfig() — TypeScript infers all value types automatically
🔑 OS keyring integration Mark sensitive fields with keyring() — secrets never touch disk
🧩 Multiple providers Choose JSON, YAML, Binary (encrypted or plain), or SQLite as the storage backend
📄 Single-file API create / load / save / delete / unlock — consistent builder-style calls
📦 Batch API loadAll / saveAll — load or save multiple configs in a single IPC round-trip
🗂️ Flexible paths Control the storage location with baseDir, options.dirName, and options.currentPath

Installation

1. Add the Rust plugin

# src-tauri/Cargo.toml

[dependencies]

tauri-plugin-configurate = "0.2"

Register it in src-tauri/src/lib.rs:

pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_configurate::init())
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

2. Add the JavaScript / TypeScript API

# npm

npm install tauri-plugin-configurate-api


# pnpm

pnpm add tauri-plugin-configurate-api


# yarn

yarn add tauri-plugin-configurate-api


# bun

bun add tauri-plugin-configurate-api

Tip: If you use the Tauri CLI, tauri add configurate handles both steps automatically.

3. Grant permissions

Add the following to your capability file (e.g. src-tauri/capabilities/default.json):

{
  "permissions": ["configurate:default"]
}

configurate:default expands to:

Permission Operation
configurate:allow-create Create a new config file
configurate:allow-load Read a config file
configurate:allow-save Write/update a config file
configurate:allow-delete Delete a config file
configurate:allow-load-all Batch load
configurate:allow-save-all Batch save
configurate:allow-unlock Inline keyring decryption

Quick Start

Step 1 — Define your schema

import { defineConfig, keyring } from "tauri-plugin-configurate-api";

const appSchema = defineConfig({
  theme: String,
  language: String,
  database: {
    host: String,
    // "password" is stored in the OS keyring, never written to disk
    password: keyring(String, { id: "db-password" }),
  },
});

defineConfig() validates at runtime that all keyring() IDs are unique within the schema.

Step 2 — Create a Configurate instance

import {
  BaseDirectory,
  Configurate,
  JsonProvider,
} from "tauri-plugin-configurate-api";

const config = new Configurate({
  schema: appSchema,
  fileName: "app.json",          // filename only, no path separators
  baseDir: BaseDirectory.AppConfig,
  provider: JsonProvider(),
  options: {
    dirName: "my-app",           // replaces the app identifier segment
    currentPath: "config/v2",    // sub-directory within the root
  },
});

The resolved path is: {AppConfig}/my-app/config/v2/app.json

Step 3 — Read and write

const KEYRING = { service: "my-app", account: "default" };

// Create — writes plain fields to disk, stores secrets in the OS keyring
await config
  .create({
    theme: "dark",
    language: "ja",
    database: { host: "localhost", password: "secret" },
  })
  .lock(KEYRING)   // KEYRING opts are required when the schema has keyring fields
  .run();

// Load (locked) — keyring fields come back as null
const locked = await config.load().run();
console.log(locked.data.database.password); // null

// Load (unlocked) — keyring fields are filled from the OS keyring
const unlocked = await config.load().unlock(KEYRING);
console.log(unlocked.data.database.password); // "secret"

// Save — same pattern as create
await config
  .save({
    theme: "light",
    language: "en",
    database: { host: "db.example.com", password: "next-secret" },
  })
  .lock(KEYRING)
  .run();

// Delete — removes the file and wipes keyring entries
await config.delete(KEYRING);

Providers

Choose the storage format when constructing a Configurate instance.

import {
  JsonProvider,
  YmlProvider,
  BinaryProvider,
  SqliteProvider,
} from "tauri-plugin-configurate-api";

// Plain JSON (human-readable)
JsonProvider()

// YAML
YmlProvider()

// Encrypted binary using XChaCha20-Poly1305
// The key is hashed via SHA-256 internally — use a high-entropy string
BinaryProvider({ encryptionKey: "high-entropy-key" })

// Unencrypted binary (compact JSON bytes, no human-readable format)
BinaryProvider()

// SQLite — all schema fields become typed columns
SqliteProvider({ dbName: "app.db", tableName: "configs" })

[!NOTE] BinaryProvider() without an encryptionKey provides no confidentiality. Use BinaryProvider({ encryptionKey }) or the OS keyring for sensitive values.


Batch Operations

Load or save multiple configs in a single IPC call with loadAll / saveAll.

const appConfig = new Configurate({
  schema: defineConfig({ theme: String }),
  fileName: "app.json",
  baseDir: BaseDirectory.AppConfig,
  provider: JsonProvider(),
});

const secretConfig = new Configurate({
  schema: defineConfig({ token: keyring(String, { id: "api-token" }) }),
  fileName: "secret.bin",
  baseDir: BaseDirectory.AppConfig,
  provider: BinaryProvider({ encryptionKey: "high-entropy-key" }),
});

// Load all — unlock a specific entry by id
const loaded = await Configurate.loadAll([
  { id: "app",    config: appConfig },
  { id: "secret", config: secretConfig },
])
  .unlock("secret", { service: "my-app", account: "default" })
  .run();

// Save all — lock a specific entry by id
const saved = await Configurate.saveAll([
  { id: "app",    config: appConfig,    data: { theme: "dark" } },
  { id: "secret", config: secretConfig, data: { token: "next-token" } },
])
  .lock("secret", { service: "my-app", account: "default" })
  .run();

Result shape

Each entry in results is either a success or a per-entry failure — a single entry failing does not abort the batch.

type BatchRunResult = {
  results: {
    [id: string]:
      | { ok: true;  data: unknown }
      | { ok: false; error: { kind: string; message: string } };
  };
};

// Access individual results
loaded.results.app;    // { ok: true, data: { theme: "dark" } }
loaded.results.secret; // { ok: true, data: { token: "..." } }

Path Resolution

Option Effect
baseDir Tauri BaseDirectory enum value (e.g. AppConfig, AppData, Desktop)
options.dirName Replaces the app-identifier segment when it is the last segment of baseDir path; otherwise appended as a sub-directory
options.currentPath Sub-directory appended after the dirName root
fileName Single filename — must not contain path separators

Example — baseDir: AppConfig, dirName: "my-app", currentPath: "v2", fileName: "settings.json":

Windows:  C:\Users\<user>\AppData\Roaming\my-app\v2\settings.json
macOS:    ~/Library/Application Support/my-app/v2/settings.json
Linux:    ~/.config/my-app/v2/settings.json

Compatibility

The following deprecated forms are still accepted in the current minor version and automatically normalized to the new API. Each emits one console.warn per process.

Deprecated form Replacement
new Configurate(schema, opts) new Configurate({ schema, ...opts })
ConfigurateFactory new Configurate({ ... })
dir baseDir
name fileName
path options.currentPath
format + encryptionKey provider: JsonProvider() / BinaryProvider({ encryptionKey })

These compatibility shims will be removed in the next minor release.


License

MIT © Crysta1221