v0.17.0 - Non-Plaintext SSH Passwords: Keychain and GPG (2026-07-11)¶
What Changed?¶
This release adds two new SSH password sources — the macOS Keychain (#41) and GPG-encrypted files (#42) — as non-plaintext alternatives to storing passwords in .env.
It also splits the growing env.py module into three focused files (#55) now that it orchestrates three password sources instead of one.
What's New¶
Main Feature: Keychain and GPG Password Sources¶
What it does:
fnb.get_ssh_password() now checks passwords in this order: host-specific FNB_PASSWORD_* env var → macOS Keychain → GPG-encrypted file → FNB_PASSWORD_DEFAULT → interactive SSH prompt.
Both new sources are opt-in (nothing changes if you don't set them up) and fail gracefully to the next source on any error.
- macOS Keychain: fnb looks up a generic password item named
fnb-{host}via thesecurityCLI. The lookup matches on service name only, so any account name works — add one manually via Keychain Access.app orsecurity add-generic-password. No-op on non-macOS platforms. - GPG-encrypted files: fnb decrypts
~/.config/fnb/passwords/{normalized_host}.gpg, falling back todefault.gpg, viagpg --decrypt --quiet --batch. Cross-platform, and works with both symmetric (passphrase) and public-key encrypted files since decryption is fully delegated togpg/gpg-agent. fnb warns (without blocking) if a password file is readable by group or other.
How to use it:
# Keychain (macOS)
security add-generic-password -s "fnb-user@remote-host" -a "user@remote-host" -w "your-password"
# GPG (any platform)
mkdir -p ~/.config/fnb/passwords
echo -n "your-password" | gpg --symmetric -o ~/.config/fnb/passwords/user_remote_host.gpg
See the Security Guide for full setup and migration steps from .env.
Installation¶
Quick Start¶
What's Different from the Last Version?¶
✅ Added¶
- macOS Keychain password source (
src/fnb/keychain.py) - GPG-encrypted password file source (
src/fnb/gpg.py) - Migration guides and troubleshooting for both in the Security Guide
🔧 Changed¶
env.py(472 lines) split intoenv.py(261 lines, orchestration only),keychain.py, andgpg.py— no behavior change, pure internal reorganizationenv.get_ssh_password()'s documented lookup order now includes the Keychain and GPG steps
🐛 Fixed¶
- None in this release
Is It Safe to Upgrade?¶
Backward Compatible: Yes.
- No configuration changes required; both new password sources are entirely opt-in
- If neither a Keychain item nor a GPG file exists for a host, behavior is identical to before this release
- The
env.pymodule split is a pure refactor —fetcher.py/uploader.py(the only external consumers) still importget_ssh_password/get_timeoutfromfnb.envunchanged
Tests Passed¶
- ✅ 188 unit and integration tests passed
- ✅ 90% code coverage (100% on both new
keychain.pyandgpg.py) - ✅ All pre-commit hooks pass (mypy, ruff, trailing whitespace, etc.)
- ✅
uv run --group docs zensical build— no new warnings from the doc changes - ✅ Manual verification on real macOS:
security add/find/delete-generic-passwordcycle, andgpg --symmetricencrypt → decrypt cycle including the graceful-fallback and permission-warning paths (throwaway test entries only, removed after verification)
Release Details¶
- Date: 2026-07-11
- Version: v0.17.0
- Commits: 7 feature/refactor/doc commits since v0.16.0, closing out issues #41, #42, and #55
9667e2b: feat(env): add macOS Keychain lookup for SSH passwordsa786550: docs: document the macOS Keychain integration33197ee: docs(env): use a meaningful account name in Keychain examples5520645: feat(env): add GPG-encrypted password files for SSH passwords58f7589: docs: document the GPG-encrypted password file integration6ba747b: refactor(env): extract macOS Keychain lookup into keychain.py1c660ad: refactor(env): extract GPG-encrypted file lookup into gpg.pyd0345b9: docs: reflect the env.py/keychain.py/gpg.py split
Next Steps¶
- Consider a dedicated
--ask-passwordflag for a masked interactive prompt, distinct from--ssh-password VALUE(which is inherently recorded in shell history once typed) — currently deferred - Phase 6 (Optional) items from #41/#42 remain out of scope: Windows Credential Manager, Linux Secret Service, multiple GPG keys, password rotation,
gpg-agentpassphrase caching UX