Your API Keys Now Live in the System Keychain, Not Plain Text
Snipset now keeps API keys and tokens in your system keychain: DPAPI on Windows, Secret Service on Linux. What moved, how it works, and what if none exists.

Snipset talks to a handful of outside services on your behalf: Ollama, your web search provider, your Telegram bot, Todoist, ElevenLabs. Each of them needs an API key or token, and until the October releases some of those keys sat in Snipset's settings table as plain text. Since Windows 0.6.857 and Linux 0.6.873, they live in your system keychain instead: encrypted with your Windows account through DPAPI, or stored through Secret Service on Linux.
This post explains which keys moved, how each platform protects them, what happens on the first start after the update, and what Snipset does when no keychain is available.
Why Plain Text API Keys Were a Problem
An API key is a password that usually does not expire on its own. Whoever holds it can use the service as you.
Snipset keeps its data in a local database on your computer. That is good for privacy, but a local file gets copied in ordinary ways: a sync folder, a disk image, a laptop you sell without wiping. If a key is stored as plain text, every copy of the file is a copy of the key.
We wanted the database to be safe to copy. After this change, the keys and tokens listed below sit in the database on Windows only as ciphertext tied to your Windows account, and on Linux, once the move is done, they are not in the database at all.
Which Keys Snipset Protects
The secret store in the desktop app (apps/desktop/src-tauri/src/secret_store/mod.rs) knows nine kinds of secrets:
| Secret | Used for |
|---|---|
| Ollama API key | Ollama requests that need a key |
| Tavily, Brave Search and TinyFish keys | Web search, one key per provider |
| Telegram bot token | Your Snipset bot |
| Todoist API token | Task sync |
| ElevenLabs API key | Read aloud with ElevenLabs voices |
| Gemini key | Cloud speech-to-text, if you turn it on |
There is also a ninth, older slot: the single web search key that earlier versions shared between providers. It is only read once, during the move (more on that below).
Every key is checked before it is saved. Keys outside the expected length are refused, ElevenLabs keys and Telegram tokens may not contain spaces, and saving an empty field deletes the key. None of these checks print the value: errors from the secret store carry fixed reasons, never the secret itself.
How the System Keychain Works on Linux
On Linux, Snipset uses the Secret Service API, the D-Bus standard written jointly by GNOME Keyring and KWallet developers. The freedesktop.org spec describes it as a way for applications to "store secrets securely in a service running in the user's login session." GNOME Keyring provides it, KeePassXC can provide it, and KDE Wallet provides it when its Secret Service option is on.
In the Rust code, the Linux backend goes through the keyring crate. Each key becomes one item with the service name snipset-secrets and its own account name, such as todoist-api-token or elevenlabs-api-key. So if you open your keyring manager, you can see exactly which keys Snipset holds, and delete one there if you want to. The Gemini key keeps the older item name it had before this change, so existing installs still find it.
One implementation detail cost us a crash before we got it right: the keyring backend runs its own small async runtime to talk to D-Bus, and starting it from inside Tauri's async runtime panics. In a release build that panic aborts the whole app. So every keychain call runs on a plain OS thread, outside any runtime (apps/desktop/src-tauri/src/utils/keyring_thread.rs).
How DPAPI Protects Keys on Windows
Windows has no Secret Service, but it has the Data Protection API (DPAPI). Snipset calls CryptProtectData on the key and stores the result in the settings table with a dpapi: prefix. Microsoft's documentation says that typically only a user with the same logon credentials can decrypt the data, and usually only on the same computer. DPAPI also adds an integrity check, so a changed ciphertext fails to decrypt instead of producing a wrong key.
We pass CRYPTPROTECT_UI_FORBIDDEN, which tells Windows to fail the call rather than show a dialog.
Android does not use this desktop secret store.
What Happens on the First Start After the Update
Older versions kept four values as plain text in the settings table: the Ollama key, the Telegram bot token, the Todoist token and the shared web search key. When Snipset starts, a background thread moves them into the secret store. For each one it:
- Checks whether the secret store already has a value. If it does, that value was saved after the update, so it is newer and stays, and the old plain text row is deleted.
- Otherwise writes the old value into the store and reads it back.
- Deletes the old plain text row only if the read-back matches.
If any step fails, the old row stays where it is and the next start tries again. Nothing is lost because of a locked keyring on one particular morning.
The shared web search key then moves to its own provider slot: to Tavily or Brave, based on your provider setting or the shape of the key. Again, the shared copy is deleted only after the new slot reads back the same value. TinyFish arrived after the shared slot, so it always had its own.
Because the move runs in the background, the Telegram bot waits up to 30 seconds for it before starting. The bot only starts when its token is in the secret store, so without the wait it could stay off on the first launch after the upgrade.
When No Keychain Is Available
On Linux, Secret Service may not be running at all (a minimal window manager without GNOME Keyring, for example), or the keyring may be locked. On Windows, DPAPI can fail. The code treats all three the same way: the secret store reports itself as unavailable.
What Snipset does next:
- Saving a key fails with a clear message. You see: "Your system keychain is not available. Install or unlock GNOME Keyring or KeePassXC, or turn on the Secret Service option in KDE Wallet."
- There is no plain text fallback. We decided a missing keychain should be a visible error, not a silent return to the old behaviour.
- Todoist sync acts as if no token is set. It treats an unreadable token as no token.
- Old rows wait. An upgrade on a machine without a keychain keeps the old plain text rows in place and retries the move on every start, so the key is not destroyed before it has somewhere safe to go.
- Backups stay clean. Secret rows are left out of backups. Restoring an older backup that still contains a plain text key moves it into the store, and if the store refuses it, the key is dropped rather than written back as plain text.
What This Does Not Protect Against
The keychain protects keys at rest. It does not protect them from a program already running under your account. DPAPI decrypts for programs running as the same Windows user, and the Secret Service spec allows an unlocked collection to be open to other applications in your session. If malware runs as you, it can ask the same way Snipset does.
What changes is the easy case: a copied database or settings file no longer carries these keys. Snipset also keeps keys out of error toasts and logs, and no stored secret is returned through IPC, the local control API or a backup.
Summary
On Windows and Linux, the keys and tokens listed above now sit in the operating system's own protection: DPAPI tied to your Windows account, or the Secret Service keychain of your desktop. Existing keys moved over on the first start, and a missing keychain shows an error instead of falling back to plain text.
Ready to connect your services without leaving keys lying around? See the Snipset plans.
