Sessions and store
The session API follows tauri-plugin-stronghold: a session (Stronghold: snapshot) holds
clients, each client has a store (key → bytes) and any number of vaults
(next guide).
import { KeyringSession } from "tauri-plugin-keyring-store-api";
const session = await KeyringSession.load("/app/secrets", "");
const client = await session.loadClient("desktop");
const store = client.getStore();
await store.insert("settings.json", new TextEncoder().encode('{"theme":"dark"}'));
const bytes = await store.get("settings.json");
const json = bytes && new TextDecoder().decode(bytes);
await session.unload();
The session path is not a file
The first argument of KeyringSession.load(path, password) is not a path on disk, on any OS. It
is a logical id: the plugin hashes it, together with the client, vault and record names, into the
account name of each OS keyring entry.
| You pass | Effect |
|---|---|
| The same string on every platform | The same namespace everywhere (the usual case). |
| Different strings | Separate namespaces, e.g. one per user or per wallet. |
Something that looks like a path, e.g. /wallet/main | Fine: it is only a label, no folder has to exist. |
Use stable ASCII-like ids. Changing the id later makes the old entries unreachable through the API (they stay in the OS store).
The password is not checked
password exists for Stronghold compatibility. It is not verified: any password opens any
session, and it encrypts nothing. Rust wipes it at once and never stores or compares it. Pass ""
unless you are migrating code that already passes one. Access control comes from the OS store and
the plugin permissions; see Security notes.
Session lifetime
KeyringSession.load(initialize) adds the path to an in-memory list of loaded sessions of this process.- Store, vault and procedure calls on a path that is not loaded fail with
session not initialized: <path>. session.unload()(destroy) removes the path from the list. The entries are not deleted.session.save()writes nothing (every write is already in the OS store) but fails like Stronghold when the session is not loaded.- The list is per process: after an app restart call
loadagain.
Clients
session.createClient(name) and session.loadClient(name) do the same thing: check that the session
is loaded and return a KeyringClient. A client needs no creation in the OS store; both exist for
Stronghold parity. client.getStore() and client.getVault(name) make no IPC call.
Store
| Method | Does |
|---|---|
get(key) | Resolves to the bytes (Uint8Array) or null. |
insert(key, value, lifetime?) | Writes the record, replacing an existing one. lifetime is ignored: OS stores have no expiry. |
remove(key) | Deletes the record and resolves to its previous bytes, or null. |
Values are number[] or Uint8Array. Store the text of JSON with TextEncoder as above.
Text or byte ids
Client, vault, record and store key names may be strings or bytes (Uint8Array, ArrayBuffer,
number[]). A byte id that equals the UTF-8 of a text id is the same id: store.get([107])
reads the record written with store.insert("k", …).
How names become entries
Every record is one OS keyring entry under your service name. Its account is built from the first 8 bytes (16 hex characters) of the SHA-256 of each name:
| Kind | Account |
|---|---|
| Store record | kp:v1:<session>:<client>:st:<key> |
| Vault record | kp:v1:<session>:<client>:v:<vault>:<record> |
So names of any length fit OS limits, and the OS store shows only hashes. The kp: prefix is
reserved: the password commands refuse it so the webview cannot
read vault keys through getPasswords.
Values are stored as Base64 text. On Windows this limits a store record or vault secret to about 960 bytes, see Windows.
The OS stores cannot list entries, so neither can the plugin: keep your own index of keys if you need one.