Where would you put them?
Anywhere else? Password managers have CLIs, operating systems have their own secure storage, and lots of command line apps can store secrets in the OS's secure storage (Windows Credential Store, Secrets Service or KWallet on Linux, macOS Keyring).
Project-specific secrets can be stored locally via something like SOPS or remotely with something like Hashicorp Vault or AWS SecretsManager.
Applications that have secrets to manage (e.g., Emacs) or are partly about secrets management (e.g., GnuPG, OpenSSH) all store their secrets somewhere else and have secure (not plaintext, sometimes not even on disk) storage options available.
There's no reason to store secrets in plain text in your shell configuration. Practically any choice you can think of is a better one. Even if you did, there's no reason you couldn't store them in a more specific file that ~/.zshrc sources, and let LLM agents read zshrc but block access to the file containing your secrets. (I wouldn't rely on permissions prompts for this, though, lol.)
Presumably a CLI-accessible password manager (like `pass`) or a GPG-encrypted file (like a netrc-style `~/.authinfo.gpg`).
I put mine in various aes encrypted file (like `~/.secrets.aes`) and then source it explicitly when needed with:
. <(aescrypt -d -o - ~/.secrets.aes)
I have a handful of aliases/functions to make it more smooth, but that's the core.Another more secure pattern: have different shell profiles that just go dynamically inject secrets from a secrets manager. Nix is a good tool for this. You have various shell profiles configurations that call your password manager cli at bootstrap (eg new terminal tab). You auth and at bootstrap of the terminal time the secret is dynamically fetched from the password manager and injected into an env var. this has advantage over other approaches mentioned here in that the secret is never stored at rest on the end user’s machine only used in flight
Into `pass`, for example:
Literally anywhere else! Your dotfiles should be publishable to github. If they aren't you're doing them wrong.
A good thing to do is organize. You can actually load different files. Here's a pretty common pattern that you'll find and it'll illustrate how to do other things
You do this for loading based on the operating system. You might want some aliases, commands, or other routines in one but not the other. For example, in my linux one I have stuff for cuda paths. You can do all sorts of things too, like make a (generically named) work file, which you don't publish to github but you load it if it exists. Then you can put all your work related aliases there and not contaminate anything else. Something like `[[ -a ${INSERT_SOME_DIR}/work.zsh ]] && source ${INSERT_SOME_DIR}/work.zsh`.You shouldn't really load secure keys this way, but others had good answers so I thought I'd at least share a more general pattern since it isn't as well known among the less terminally inclined.