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
- Features
- Platform support
- Installation
- Usage
- Cargo features
- Permissions
- Relationship to Stronghold
- Development
- Testing
- Contributing
- Partners
- License
Features
- Cross-platform keyring via
keyring-core1.xand 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 thecryptofeature is enabled. - Optional
cryptofeature (default): SLIP10/BIP39/Ed25519 viaiota-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)
# or from crate published on crates.io after release
Manual — Rust
Disable crypto (storage IPC only):
= { = "0.1", = false }
Manual — JavaScript
Usage
Backend
Custom service name (defaults to identifier in tauri.conf.json):
new
.service
.build
Rust — access the store from commands / plugins
use Manager;
use KeyringExt;
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
Rustdoc logo (after push to main): PNG is generated from assets/docs-logo.svg:
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