@@ -259,6 +259,87 @@ Hermes files are managed in:
259259- ` ~/.hermes/state.db` — session history database
260260- ` ~/.hermes/cron/jobs.json` — scheduled tasks
261261
262+ # # Secrets provider
263+
264+ By default, API keys live in ` ~/.hermes/.env` (the ** env** provider). No
265+ configuration is needed — this is byte-for-byte the historical behavior, and
266+ nothing changes for you.
267+
268+ If you' d rather not keep keys in a plaintext `.env`, the opt-in **command**
269+ provider resolves them by running a helper command you configure. Resolution
270+ order everywhere is: `process.env` → `.env` → provider → unset.
271+
272+ Per-key helper (the requested key name arrives as `$HERMES_SECRET_KEY`):
273+
274+ ```yaml
275+ # ~/.hermes/config.yaml
276+ secrets:
277+ provider: command
278+ command: secret-tool lookup hermes "$HERMES_SECRET_KEY"
279+ ```
280+
281+ Or a helper that dumps a dotenv blob (e.g. a vault that unseals into tmpfs):
282+
283+ ```yaml
284+ secrets:
285+ provider: command
286+ command: "cat /run/user/1000/hermes-secrets.env"
287+ ```
288+
289+ The helper' s stdout may be either a single bare value (per-key helpers) or
290+ ` KEY=VALUE` lines (dotenv dumps); both shapes are auto-detected.
291+
292+ # ## Vault / secret manager integration (no TPM required)
293+
294+ The ` command` provider is ** vault-agnostic** — it runs whatever helper you
295+ configure and reads its stdout. The helper is the only thing that needs to
296+ talk to your secret store. If you don' t have a TPM-sealed keyfile, any of
297+ these work without code changes to Hermes:
298+
299+ - **KeePassXC (password-only DB, no keyfile):** point `secrets.command` at a
300+ small `kpxc-export.sh` script that does
301+ `keepassxc-cli ls ~/secrets/hermes.kdbx <<<"$KPXC_PASSWORD"` and dumps
302+ the relevant group as dotenv. Prompt the user for the master password
303+ once per session.
304+ - **GnuPG with a passphrase-only key:** `gpg --batch --passphrase-fd 0
305+ --decrypt ~/.keys/api-keys.gpg` works directly as the `command` value.
306+ Pass the passphrase via a file descriptor or env var, never argv.
307+ - **`pass` (the standard unix password manager):**
308+ `command: "pass show hermes/$HERMES_SECRET_KEY"` for a per-key helper,
309+ or a small wrapper script for a dotenv dump.
310+ - **`secret-tool` (libsecret/Gnome Keyring):**
311+ `command: "secret-tool lookup hermes $HERMES_SECRET_KEY"` (already
312+ shown above as the canonical per-key example).
313+ - **Bitwarden CLI:** `bw get item "$HERMES_SECRET_KEY" | jq -r .notes`
314+ (after `bw unlock` in the session).
315+ - **1Password CLI:** `op read "op://vault/$HERMES_SECRET_KEY/credential"`.
316+ - **Plain env file with user-managed permissions:**
317+ `command: "cat ~/.config/hermes/secrets.env"` with `chmod 600` and
318+ the file owned by your user. Not as secure as a vault, but better than
319+ a world-readable `.env`.
320+
321+ The point: **any helper that prints a value (per-key) or a dotenv blob
322+ (list-mode) on stdout will work**, and Hermes imposes a 3-second timeout
323+ and 1 MiB output cap on the helper so a misbehaving one can' t wedge the
324+ app. The provider makes no assumptions about TPM, FIDO2, smart cards,
325+ or platform keychains.
326+
327+ Security model:
328+
329+ - The command string is your own configuration — same trust level as ` .env` .
330+ It runs via ` /bin/sh -c` , so the command provider is POSIX-only
331+ (Linux/macOS); Windows stays on the env provider.
332+ - The helper inherits the process environment plus ` HERMES_SECRET_KEY` ; the
333+ key name is passed as data, never interpolated into the shell string.
334+ - Hard 3-second timeout (resolution is synchronous on the main process — keep
335+ helpers fast and non-interactive), 1 MiB output cap, and stderr is discarded.
336+ - Resolved values are never logged or written to disk; failures degrade to
337+ " key unset" , logging only exit code/signal.
338+ - The gateway-spawn broadcast uses a single `list ()` call, never a per-key
339+ helper loop.
340+
341+ Source of truth: [` src/main/secrets/` ](src/main/secrets/).
342+
262343# # Tech Stack
263344
264345- ** Electron** 39 — cross-platform desktop shell
0 commit comments