Skip to content

Store a secret in the OS keychain

KeyringStore writes to the macOS Keychain, the Windows Credential Manager, or the Linux kernel keyring, depending on the target it was built for.

Write a secret

use rtb_credentials::{CredentialStore, KeyringStore, SecretString};

let store = KeyringStore::new();
store.set("mytool", "anthropic", SecretString::from("sk-ant-...".to_string())).await?;

The two strings are the keychain's service and account identifiers. service is conventionally your tool's name, account the provider or user. Neither is interpreted by this crate — they are passed straight to the platform.

set overwrites an existing entry without complaint.

Read it back

let secret = store.get("mytool", "anthropic").await?;

An absent entry returns CredentialError::NotFound with the name "mytool/anthropic". Any other backend failure returns CredentialError::Keychain carrying the platform's message.

Delete it

store.delete("mytool", "anthropic").await?;

Deleting an entry that does not exist returns Ok(()). There is no need to check for existence first, and no way to distinguish "deleted" from "was not there".

Point a config at the entry you just wrote

anthropic:
  keychain:
    service: mytool
    account: anthropic

Both subkeys are required — omitting either is a config load error, not a resolution-time miss.

Run it on a Tokio runtime

Every KeyringStore method wraps a blocking call in tokio::task::spawn_blocking, which panics outside a Tokio runtime.

#[tokio::main]
async fn main() -> miette::Result<()> {
    let store = KeyringStore::new();
    // ...
    Ok(())
}

There is no way to use KeyringStore on async-std or smol. If you need one of those, use EnvStore or write your own CredentialStore.

Expect this to fail on servers, containers and CI

Keychain access needs a platform keychain to be present and unlocked. Headless Linux servers, containers and CI runners generally have neither, and get fails there with CredentialError::Keychain rather than NotFound.

That distinction matters when the credential is resolved through a Resolver: a Keychain error aborts the whole resolution, so a configured fallback_env will not rescue it. On machines without a usable keychain, leave the keychain key out of the config entirely and use an environment variable.

On Linux there is a second constraint. The default backend is kernel keyutils, whose entries vanish when the session ends — a secret written in one login is gone after a reboot. See enabling persistent Linux storage.

Handle the platform differences you cannot avoid

Platform Persistence Common failure
macOS across reboots user declines the unlock prompt
Windows across reboots rare; credential manager is generally available
Linux (default) session only no session keyring in a container
Linux (linux-persistent) across reboots no D-Bus session or no keyring daemon

A tool that must work everywhere should treat the keychain as one optional source among four, not as its storage layer.