Skip to main content
tauri-plugin-configurate applies several layers of defence-in-depth: it restricts which filesystem directories IPC payloads may address, keeps sensitive fields out of config files via the OS keychain, limits the data it reads in one call, and uses atomic writes to prevent corruption. Understanding each layer helps you configure and use the plugin safely in production.

BaseDirectory restrictions

By default the plugin allows IPC payloads to reference only app-scoped directories: AppConfig, AppData, AppLocalData, AppCache, AppLog, Resource, and Temp. Requests that name any other directory — such as Home, Desktop, or Document — are rejected before any filesystem operation runs. This restriction prevents a compromised or misbehaving frontend from using the plugin’s commands to read or overwrite arbitrary paths on the user’s machine. The plugin validates the baseDir field on every inbound IPC payload against the configured allowlist. If your application legitimately needs access to additional directories, expand the allowlist explicitly in Rust rather than disabling the restriction entirely:
// src-tauri/src/lib.rs

// Allow specific additional directories
tauri_plugin_configurate::Builder::default()
    .allowed_base_directories([
        tauri::path::BaseDirectory::AppConfig,
        tauri::path::BaseDirectory::Document,
    ])
    .build()
If you need to disable the restriction entirely — for example, during development or for a power-user tool that intentionally manages arbitrary paths — you can opt out, but this is not recommended for consumer apps:
// Disables all BaseDirectory validation — use with caution
tauri_plugin_configurate::Builder::default()
    .allow_any_base_directory()
    .build()

Keyring security

Fields marked with keyring() in your schema are stored in the operating system’s platform keychain rather than in the config file on disk:
  • macOS — Keychain Services
  • Windows — Windows Credential Manager
  • Linux — Secret Service (via libsecret / KWallet)
This means a keyring field value is never written to the filesystem. When the plugin reads a keyring field from an IPC payload, it validates that the value property is empty — it will not accept a value supplied by the client and write it to the keychain as-is through the unlock path, preventing a client from spoofing a secret it does not already have. The configurate:allow-unlock permission grants access to the unlock command, which reads secrets from the keychain and returns them to the frontend. This permission is not included in configurate:default. Grant it only to capability sets that genuinely require access to decrypted keyring values. UnlockedConfig.lock() sets the internal data reference to null, revoking JavaScript-level access to decrypted values through that instance:
const unlocked = await config.load().unlock(KEYRING);
const secret = unlocked.data.database.password; // "secret"

unlocked.lock();
unlocked.data; // throws — access revoked
UnlockedConfig.lock() is an API-level guard, not a cryptographic memory wipe. JavaScript’s garbage collector controls memory reclamation. Treat it as a way to enforce access discipline in your code, not as a guarantee that sensitive bytes are immediately zeroed.

Encryption key over IPC

When you use BinaryProvider with an encryptionKey, that key is included in the Tauri IPC payload only for commands that actually need to decrypt or encrypt content: load, create, save, and patch. It is deliberately excluded from the payloads for delete and exists, which operate on the file without reading its encrypted content. The key never leaves the machine — Tauri IPC is a local in-process bridge, not a network transport. However, DevTools in a Tauri window can inspect IPC messages, which would expose the key to anyone with access to the developer console. Recommendation: Disable or restrict DevTools in production builds to prevent IPC inspection. Tauri’s app.windows[].devtools configuration and #[cfg(debug_assertions)] guards are the standard mechanisms for this.

YAML import safety

YAML supports anchors and aliases, a feature that lets a single small document expand into an arbitrarily large in-memory structure when parsed. A malicious or accidentally crafted YAML file can cause the parser to allocate gigabytes of memory (a “billion laughs” attack).
Never import YAML from untrusted sources. If your application allows users to supply configuration files, reject YAML and accept only JSON or TOML, both of which have no equivalent expansion mechanism. Even maxReadBytes will not protect you if the YAML expands significantly before the size check is applied.
// Safe: import from a known-good source
await config.importFrom(trustedJsonString, "json");

// Risky: user-supplied YAML can expand via anchors/aliases
// await config.importFrom(userSuppliedContent, "yml"); // avoid this

File write safety

All file writes use an atomic replace strategy via tempfile::persist (including on Windows). The plugin writes the new content to a temporary file in the same directory, then atomically renames it over the target path. This means:
  • A crash or power failure during a write leaves the previous file intact rather than producing a corrupt partial write.
  • There is no window where the file exists in a half-written state.
  • The rename is atomic at the filesystem level, which avoids the TOCTOU (time-of-check to time-of-use) race that would exist with a remove followed by a rename.
External processes writing to the same config file concurrently are outside the scope of the plugin’s advisory file lock and can still produce conflicts. Avoid having multiple processes write to the same config file simultaneously. For critical configuration, you can enable rolling backups by setting backup: true in the Configurate constructor. Before each write, the plugin copies the current file to .bak1, rotating older backups to .bak2 and .bak3. Backup files are automatically deleted when the application exits cleanly.
const config = new Configurate({
  schema,
  fileName: "app.json",
  baseDir: BaseDirectory.AppConfig,
  provider: JsonProvider(),
  backup: true, // creates .bak1–.bak3 before each write
});

IPC safety limits

The plugin enforces several hard limits on inbound IPC data to prevent abuse or accidental runaway:
LimitValueWhat it protects
Dot-path segments64Prevents deeply nested paths from causing excessive recursion
Array index cap10,000Bounds array traversal in keyring path resolution
Batch entries per request128Caps the size of loadAll / saveAll / patchAll payloads
Config file / import size16 MiB (default)Prevents loading excessively large files into memory
You can raise or lower the read limit via the Rust builder or tauri.conf.json. Lowering it is appropriate if your configs are always small and you want an extra guard against unexpected growth:
tauri_plugin_configurate::Builder::default()
    .max_read_bytes(4 * 1024 * 1024) // 4 MiB
    .build()
{
  "plugins": {
    "configurate": {
      "maxReadBytes": 4194304
    }
  }
}

Production checklist

Use the checklist below before shipping a production build. Each item maps to one of the security properties described on this page.
Work through this checklist for every app-facing capability file, not just the default one. Overly broad permissions granted to a secondary window are just as risky as broad permissions on the main window.
  • Grant only the permissions you need. Start with configurate:default and add configurate:allow-unlock only if the window actually needs to read keyring-backed fields.
  • Use app-scoped BaseDirectory values. Stick to AppConfig, AppData, AppLocalData, AppCache, AppLog, Resource, or Temp unless you have a documented reason to expand the allowlist.
  • Use kdf: "argon2" for user-supplied keys. Any encryptionKey that originates from a user-typed password or low-entropy source must use Argon2id. The default "sha256" KDF is only safe for high-entropy random keys.
  • Restrict DevTools in production builds. Tauri DevTools allow inspection of IPC messages, which would expose the encryptionKey and keyring service/account values. Guard DevTools behind #[cfg(debug_assertions)] or Tauri’s devtools window configuration.
  • Never import YAML from untrusted sources. Restrict import to JSON or TOML for any content supplied by users or fetched from the network.
  • Enable backup: true for critical config files. Rolling backups provide a recovery path if a write is interrupted or a migration produces unexpected results.
  • Do not share config files between concurrent processes. The plugin’s advisory lock does not prevent external processes from writing to the same path simultaneously. Design your app so only one process owns each config file.