將 KeePassXC 變成 Emacs 的鑰匙圈
現在 Emacs 的 AI 生態也是好起來了:要日常對話我們有 gptel;要 AI 補全我們有 Minuet;要 AI 輔助編程我們有基於 Aider 的 Aidermacs。
大部分 Emacs 包都會推薦使用 auth-source
從 ~/.authinfo
裏面讀取 API key。在配合 GPG 加密的情況下,這個方法的安全性應該是沒什麼問題,但非 Emacs 生態的軟件基本不會用這個方法讀 API key,把各種軟件用到的 API key 滿地亂放着實是不怎麼優雅。
之前在把弄 KDE 軟件的時候,就從 KDE Wallet 順藤摸瓜了解到 Secrets API,它大概就是一個類似 iCloud Keychain 的密碼儲存器。除了 KDE Wallet,像是 GNOME Keyring 和 KeePassXC 也提供了相應的 API,我選擇用 KeePassXC 的原因主要還是我本來就用 KeePassXC 來做密碼管理器,而且 KeePass 的單文件密碼庫遷移起來也很方便。
要使用 KeePassXC 提供 Secrets API,我們需要先停用其他軟件的 Secrets API,然後在 KeePassXC 的「保密服務整合」設定中勾選「啟用 KeePassXC 與 Freedesktop.org 保密服務的整合」:
回到 Emacs,我們可以用 secrets
這個內建包來與 Secrets API 交互——有些使用 Secrets API 的軟件會搞些複雜的數據結構,但我在 Emacs 裏面基本只會用它來存 API key,所以當成最基本的存密碼就可以了——其中用得最多的應該是 secrets-create-item
和 secrets-get-secret
。
為了預防因為沒有 API key 而報錯,我們可以寫一個 helper function 來獲取 API key,它會在 API key 不存在的時候,要求用戶立即輸入一個並儲存到 KeePassXC 中:
(defun secrets-get-or-create-secret (collection item prompt)
"Return the secret of item labeled ITEM in COLLECTION if it exists, \
otherwise create a new item and return the password, where the password is read with PROMPT."
(interactive)
(unless (member item (secrets-list-items collection))
(secrets-create-item collection item (read-passwd prompt)))
(secrets-get-secret collection item))
在 gptel 中,我們可以這樣接入 KeePassXC 中的 API key:
(setq gptel-backend
(gptel-make-openai "OpenRouter"
:host "openrouter.ai"
:endpoint "/api/v1/chat/completions"
:stream t
:key (lambda ()
(secrets-get-or-create-secret "Main" "emacs-openrouter-api-key" "OpenRouter API Key: "))
:models '(google/gemini-2.5-pro-preview)))
之後嘗試用 M-x gptel
來新建一個對話窗口,此時應該會提示你輸入 API key,輸入後就會發現 KeePassXC 中新建了一個密碼:
之後每次 gptel 用到 API key 的時候,都會直接調用 KeePassXC 中這個 API key。
類似地,我們也可以給其他 Emacs 包接入這個 API key,比如說利用 Aidermacs 給 Aider 提供 API key:
(add-hook 'aidermacs-before-run-backend-hook
(lambda ()
(setenv "OPENROUTER_API_KEY" (secrets-get-or-create-secret "Main" "emacs-openrouter-api-key" "OpenRouter API Key: "))))
BTW,每次用到 Secrets API 的時候,KeePassXC 都會彈出通知,如果嫌太煩可以在 KeePassXC 的設定裏面把通知關掉。