tauri-plugin-keyring-store 0.1.4

Stronghold-inspired secrets and crypto helpers backed by the OS credential store (keyring-core).
Documentation

npm version Crates.io Documentation GitHub issues GitHub stars Donate

Tauri Plugin Keyring Store

Store secrets and wallet-style procedures using the OS credential store (macOS Keychain, Windows Credential Manager, Linux Secret Service, Android Keystore, iOS Data Protection). The guest API mirrors tauri-plugin-stronghold sessions, clients, store, vault, and crypto procedures — but there is no encrypted snapshot file: everything maps to hashed keyring entries under your app service name (defaults to the Tauri bundle identifier).


Table of contents

  1. Features
  2. Platform support
  3. Installation
  4. Usage
  5. Cargo features
  6. Permissions
  7. Relationship to Stronghold
  8. Development
  9. Testing
  10. Contributing
  11. Partners
  12. License

Features

  • Cross-platform keyring via keyring-core 1.x and official backend crates (native stores only — no silent in-memory fallback).
  • Rust-first API: app.keyring() exposes [KeyringPlugin] with [KeyringStore] for backend code without IPC.
  • Stronghold-shaped JS API: KeyringSession, KeyringClient, KeyringStoreView, KeyringVault + SLIP10 / BIP39 / Ed25519 procedures when the crypto feature is enabled.
  • Optional crypto feature (default): SLIP10/BIP39/Ed25519 via iota-crypto; secrets stored as Base64 in the OS vault.

Platform support

Platform Backend
macOS Login Keychain
iOS Protected (Data Protection) Keychain
Windows Credential Manager
Linux Secret Service (DBus; crypto-rust — no host OpenSSL required to build)
Android Android Keystore + SharedPreferences

Linux desktops need a Secret Service (e.g. GNOME Keyring / KWallet). Headless CI often has no user session — avoid relying on the live keyring there (see Testing). On Android, transitive deps may pull OpenSSL; your app may need openssl-sys with vendored for cross-builds (see Subly-style setups).


Installation

Automatic (recommended)

cargo add tauri-plugin-keyring-store
# or from crate published on crates.io after release

Manual — Rust

cd src-tauri
cargo add tauri-plugin-keyring-store

Disable crypto (storage IPC only):

tauri-plugin-keyring-store = { version = "0.1", default-features = false }

Manual — JavaScript

pnpm add tauri-plugin-keyring-store-api

Usage

Backend

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

Custom service name (defaults to identifier in tauri.conf.json):

tauri_plugin_keyring_store::Builder::new()
  .service("com.mycompany.myapp.credentials")
  .build()

Rust — access the store from commands / plugins

use tauri::Manager;
use tauri_plugin_keyring_store::KeyringExt;

#[tauri::command]
fn save_api_token(app: tauri::AppHandle, token: String) -> Result<(), String> {
  app.keyring().store
    .set_password("manual.example.token", &token)
    .map_err(|e| e.to_string())
}

Sessions opened from the frontend (initialize) are tracked separately; low-level [KeyringStore] calls use whatever account string you pass.

Frontend (Stronghold-like flow)

import { KeyringSession, KeyringClient } from 'tauri-plugin-keyring-store-api'

const session = await KeyringSession.load('/logical/path', 'ignored-password')
const client = await session.createClient('main')
await client.getStore().insert('prefs', Array.from(new TextEncoder().encode('{}')))
await session.unload()

initialize accepts a password for Stronghold API compatibility; it is zeroed on the Rust side and does not unlock a file snapshot.


Direct account API (bulk, exists, naming, backup)

Raw account strings are the OS keyring entry names under your app service (defaults to the bundle identifier). These commands avoid session hashing — useful for app-controlled keys.

IPC command Purpose
get_passwords Read many UTF-8 secrets (parallel Vec, max 256 accounts per call).
set_passwords Write many { account, secret } pairs.
delete_passwords Delete many accounts.
password_exists true if a non-empty secret exists (exists_nonempty).
export_passwords_plain / import_passwords_plain JSON backup blob over IPC.
export_passwords_encrypted / import_passwords_encrypted Argon2id + ChaCha20-Poly1305 envelope (always compiled; independent of the crypto feature).

Naming (application convention): use prefix.name with a single dot — helpers join_prefix / split_prefixed in Rust, and joinKeyPrefix / splitKeyPrefix in guest-js. The OS keyring still does not support listing by prefix; keep your own index of logical keys if needed.

Security — plaintext backup: export_passwords_plain / import_passwords_plain move secrets in the clear across IPC to the webview. Use only in trusted UI flows, or prefer export_passwords_encrypted / disk encryption.

Guest-js exports: getPasswords, setPasswords, deletePasswords, passwordExists, exportPasswordsPlain, importPasswordsPlain, exportPasswordsEncrypted, importPasswordsEncrypted.


Cargo features

Feature Default Description
crypto yes SLIP10 / BIP39 / Ed25519 execute_procedure via iota-crypto. Encrypted backup (Argon2 + ChaCha) is always available without this flag.

Permissions

Use keyring-store:default or granular keyring-store:allow-* (see permissions/default.toml). Commands: plugin:keyring-store|<command>.


Relationship to tauri-plugin-stronghold

Stronghold This plugin
Password-derived snapshot No snapshot file; OS stores secrets
save() writes snapshot save() is a no-op (compat)
Procedures in Stronghold VM In-process crypto; outputs in keyring

Development

cargo fmt --all
cargo clippy --all-targets --all-features -- -D warnings
cargo test
cargo build --no-default-features

pnpm install
pnpm build
pnpm test

Rustdoc logo (after push to main): PNG is generated from assets/docs-logo.svg:

rsvg-convert -w 128 -h 128 assets/docs-logo.svg -o assets/docs-logo.png

Testing

  • Rust: cargo test — deterministic account-key tests and serde roundtrips do not need D-Bus. Tests that call the real OS store are #[ignore]; run locally where Secret Service / Keychain is available.
  • JavaScript: pnpm test (Vitest) mocks @tauri-apps/api/core.

Contributing

Issues and pull requests are welcome on GitHub.


Partners

Contributions and sponsorship help maintain this and related plugins. Thank you for your support.


License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

SPDX-License-Identifier: MIT OR Apache-2.0