Rust backend
The plugin's state is available on every App, AppHandle and WebviewWindow through the
KeyringExt trait. Full API: docs.rs.
use tauri_plugin_keyring_store::KeyringExt;
#[tauri::command]
fn save_api_token(app: tauri::AppHandle, token: String) -> Result<(), String> {
app.keyring()
.store
.set_password("app.api_token", &token)
.map_err(|e| e.to_string())
}
app.keyring() returns &KeyringPlugin:
| Field | Type | What |
|---|---|---|
store | Arc<KeyringStore> | OS store access for the plugin's service name. |
sessions | SessionRegistry | Session paths loaded with initialize (insert / remove / contains). |
KeyringStore
| Method | Does |
|---|---|
new(service) | Handle for one service name. No I/O until the first call. |
service() | The service name. |
set_password(account, &str) / get_password(account) | UTF-8 secret; Ok(None) if missing. |
set_bytes(account, &[u8]) / get_bytes(account) | Bytes, stored as Base64 text. |
delete(account) | Deletes; a missing entry is Ok(()). |
exists_nonempty(account) | true if the secret is not empty after trimming. |
account_store_key(session, client, key) | Account of a session store record (kp:v1:…:st:…). |
account_vault_record(session, client, vault, record) | Account of a vault record (kp:v1:…:v:…). |
account_raw(session, client, suffix) | Account for a free-form key under a session and client (kp:v1:…:x:…). |
with_store(store) | Use another keyring-core store for this handle. |
availability(), *_for_background | Locked-device helpers, see below. |
Rust code is not limited by the kp: guard: it can read and
write any account, including the session records.
Calls block
KeyringStore methods block: macOS can show a Keychain prompt, Linux talks to the Secret Service over
D-Bus and may wait for an unlock prompt. In async commands, run them on a blocking thread:
#[tauri::command]
async fn read_token(app: tauri::AppHandle) -> Result<Option<String>, String> {
tauri::async_runtime::spawn_blocking(move || app.keyring().store.get_password("app.api_token"))
.await
.map_err(|e| e.to_string())?
.map_err(|e| e.to_string())
}
The plugin's own commands do this already.
Session records from Rust
The same hashing as the IPC store commands, so JavaScript sees what Rust writes:
use tauri_plugin_keyring_store::{BytesDto, KeyringExt};
let kr = app.keyring();
let client = BytesDto::Text("desktop".into());
let account = kr.store.account_store_key("/app/secrets", &client, "prefs");
kr.store.set_bytes(&account, b"{}")?;
// JS: (await (await KeyringSession.load("/app/secrets", "")).loadClient("desktop")).getStore().get("prefs")
Service name
init() uses the app identifier. To set it:
tauri_plugin_keyring_store::Builder::new()
.service("com.example.myapp.keyring")
.build()
A standalone KeyringStore::new("…") can use any other service, e.g. to share entries with another app
or tool that uses the same service and account names.
Custom credential store
with_store points one handle at any keyring-core store instead of the
process-wide default for the platform. The plugin registered with init() keeps the default store.
use tauri_plugin_keyring_store::KeyringStore;
let store = KeyringStore::new("com.example.app")
.with_store(linux_keyutils_keyring_store::Store::new()?);
store.set_password("app.token", "secret")?;
Useful on Linux servers without a Secret Service (linux-keyutils-keyring-store
never prompts, but keeps secrets only for the login session) and in tests (keyring_core::mock::Store).
Locked device and background reads
On iOS the keychain can be unreadable while the device is locked, see iOS.
| Call | When locked |
|---|---|
get_password, get_bytes, exists_nonempty | Err(Error::KeychainLocked) |
get_password_for_background, get_bytes_for_background | Ok(None) |
exists_nonempty_for_background | Ok(false) |
availability() | KeyringAvailability::Locked |
use tauri_plugin_keyring_store::{Error, KeyringAvailability, KeyringStore};
fn sync(store: &KeyringStore) -> Result<(), Error> {
if store.availability() == KeyringAvailability::Locked {
return Ok(()); // try again later
}
let token = store.get_password_for_background("oauth-token")?;
Ok(())
}
On other platforms availability() always returns Available. These helpers have no IPC command;
over IPC a locked keychain arrives as the error string keychain locked or protected data unavailable.
Errors
tauri_plugin_keyring_store::Error is #[non_exhaustive] and serializes to a plain string over IPC.
Messages never contain secrets, key material or mnemonic words.
| Variant | Message |
|---|---|
SessionNotInitialized(path) | session not initialized: <path> |
NoEntry | credential entry not found |
Init(reason) | plugin initialization failed: <reason>: the OS store could not start. |
KeychainLocked | keychain locked or protected data unavailable |
Keyring(reason) | keyring error: <reason>: any other OS store error. |
Encoding(reason) | encoding error: <reason>: bad Base64 in an entry, bad backup format. |
Crypto(reason) | crypto procedure error: <reason> |
Naming(reason) | naming / validation error: <reason>: account rules, item limits, naming helpers. |
When the OS store fails to start (e.g. no D-Bus session yet), the failure is not cached: the next call after a 2-second pause tries again.