keychain-bridge
Facilitates secure management of secrets using macOS Keychain, enhancing security and compatibility with bash tools.
Install this skill
Security score
The keychain-bridge skill was audited on Mar 8, 2026 and we found 85 security issues across 3 threat categories, including 42 high-severity. Review the findings below before installing.
Categories Tested
Security Issues
Template literal with variable interpolation in command context
| 135 | ```bash |
Python subprocess execution
| 227 | subprocess.run(["/opt/homebrew/bin/python3.14", "-c", |
Access to hidden dotfiles in home directory
| 34 | Action: python3 SKILL_DIR/scripts/migrate_secrets.py --dir ~/.openclaw/secrets/ --account moltbot --dry-run |
Access to hidden dotfiles in home directory
| 40 | Action: python3 SKILL_DIR/scripts/audit_secrets.py --dir ~/.openclaw/secrets/ --account moltbot |
Access to hidden dotfiles in home directory
| 85 | python3 SKILL_DIR/scripts/migrate_secrets.py --dir ~/.openclaw/secrets/ --account moltbot --dry-run |
Access to hidden dotfiles in home directory
| 87 | python3 SKILL_DIR/scripts/migrate_secrets.py --dir ~/.openclaw/secrets/ --account moltbot |
Access to hidden dotfiles in home directory
| 120 | MY_SECRET=$(cat ~/.openclaw/secrets/my-service-name) |
Access to hidden dotfiles in home directory
| 154 | python3 SKILL_DIR/scripts/audit_secrets.py --dir ~/.openclaw/secrets/ --account moltbot |
Access to hidden dotfiles in home directory
| 205 | os.makedirs(os.path.expanduser("~/.my-app/secrets"), exist_ok=True) |
Access to hidden dotfiles in home directory
| 206 | path = os.path.expanduser("~/.my-app/secrets/SERVICE") |
Access to system keychain/keyring
| 2 | name: keychain-bridge |
Access to system keychain/keyring
| 3 | description: Manage secrets via macOS Keychain instead of plaintext files. Migrate existing secrets, read/write keychain entries, bridge to files for bash tools, audit for leaks, diagnose access issue |
Access to system keychain/keyring
| 4 | homepage: https://github.com/moltbot/keychain-bridge |
Access to system keychain/keyring
| 16 | tags: ["keychain", "macos", "secrets", "credentials", "tahoe", "migration"] |
Access to system keychain/keyring
| 19 | # Keychain Bridge |
Access to system keychain/keyring
| 23 | - "migrate secrets to keychain" / "move secrets" |
Access to system keychain/keyring
| 24 | - "check keychain health" / "keychain status" |
Access to system keychain/keyring
| 27 | - "store secret" / "write to keychain" |
Access to system keychain/keyring
| 28 | - "keychain not working" / "security find-generic-password hangs" |
Access to system keychain/keyring
| 33 | User: "Migrate my secrets to the keychain" |
Access to system keychain/keyring
| 36 | User: "Check if the keychain bridge is healthy" |
Access to system keychain/keyring
| 37 | Action: Run keychain health check (test write/read/delete cycle) |
Access to system keychain/keyring
| 43 | Manage secrets via macOS Keychain instead of plaintext files. Eliminates plaintext credential storage while maintaining compatibility with bash-based tools through a file-bridge architecture. |
Access to system keychain/keyring
| 47 | The `keyring` Python library must be installed for each Python version that will access secrets: |
Access to system keychain/keyring
| 50 | pip3 install keyring |
Access to system keychain/keyring
| 52 | /usr/bin/python3 -m pip install keyring |
Access to system keychain/keyring
| 53 | /opt/homebrew/opt/[email protected]/bin/python3.14 -m pip install --break-system-packages keyring |
Access to system keychain/keyring
| 56 | ## Check Keychain Health |
Access to system keychain/keyring
| 58 | Verify the keychain bridge is working correctly: |
Access to system keychain/keyring
| 62 | import keyring |
Access to system keychain/keyring
| 64 | keyring.set_password('keychain-bridge-test', 'test', 'hello') |
Access to system keychain/keyring
| 66 | val = keyring.get_password('keychain-bridge-test', 'test') |
Access to system keychain/keyring
| 69 | keyring.delete_password('keychain-bridge-test', 'test') |
Access to system keychain/keyring
| 70 | print('Keychain health: OK') |
Access to system keychain/keyring
| 78 | Migrate plaintext secret files to macOS Keychain. The migration tool: |
Access to system keychain/keyring
| 92 | ### Group A — Keychain Only |
Access to system keychain/keyring
| 93 | Python scripts read directly via `keychain_helper.get_secret(service)`. No file on disk. |
Access to system keychain/keyring
| 96 | Bash scripts cannot reliably use Python keyring as a subprocess (see **Known Issues**). For these, a boot-time bridge script populates files from the keychain: |
Access to system keychain/keyring
| 103 | This reads each Group B secret from keychain and writes it to a `chmod 600` file that bash scripts can `cat`. |
Access to system keychain/keyring
| 111 | from keychain_helper import get_secret |
Access to system keychain/keyring
| 116 | The helper tries keychain first, falls back to file read. |
Access to system keychain/keyring
| 133 | **Critical**: Inject from ALL Python versions on the system. Keychain ACLs are per-binary — an item created by Python 3.9 cannot be read by Python 3.14 unless both binaries are in the ACL. |
Access to system keychain/keyring
| 143 | $py -c "import keyring; keyring.set_password('SERVICE', 'ACCOUNT', 'VALUE')" |
Access to system keychain/keyring
| 151 | Check for unexpected plaintext secret files and verify keychain health: |
Access to system keychain/keyring
| 159 | - Keychain items that exist but can't be read (ACL issues) |
Access to system keychain/keyring
| 160 | - Files that exist but aren't in keychain (unmigrated) |
Access to system keychain/keyring
| 161 | - Keychain library installation status per Python version |
Access to system keychain/keyring
| 166 | **macOS Tahoe 26.x regression.** The `security` CLI hangs indefinitely (or returns exit code 36) when reading keychain items, even after `security unlock-keychain`. This affects ALL CLI-based keychain |
Access to system keychain/keyring
| 168 | **Fix**: Use Python `keyring` library instead. It uses the Security framework C API via ctypes, bypassing the broken CLI entirely. |
Access to system keychain/keyring
| 170 | ### Python keyring returns None or raises errSecInteractionNotAllowed (-25308) |
Access to system keychain/keyring
| 171 | This happens when running from an SSH session. The keychain requires a GUI session (SecurityAgent) context. |
Access to system keychain/keyring
| 175 | **Fix (SSH write — ctypes unlock)**: The `security unlock-keychain -p` CLI command is also broken on Tahoe (returns "incorrect passphrase" with correct password). Use the Security framework C API via |
Access to system keychain/keyring
| 179 | import ctypes, ctypes.util, keyring |
Access to system keychain/keyring
| 183 | keychain = ctypes.c_void_p() |
Access to system keychain/keyring
| 184 | path = b"/Users/USERNAME/Library/Keychains/login.keychain-db" |
Access to system keychain/keyring
| 185 | Security.SecKeychainOpen(path, ctypes.byref(keychain)) |
Access to system keychain/keyring
| 187 | Security.SecKeychainUnlock(keychain, ctypes.c_uint32(len(pw)), pw, ctypes.c_bool(True)) |
Access to system keychain/keyring
| 189 | # Now keyring works — but ONLY within this same process |
Access to system keychain/keyring
| 190 | keyring.set_password("SERVICE", "ACCOUNT", "VALUE") |
Access to system keychain/keyring
| 191 | print("OK" if keyring.get_password("SERVICE", "ACCOUNT") else "FAIL") |
Access to system keychain/keyring
| 202 | # After keyring.set_password() succeeds in the same process: |
Access to system keychain/keyring
| 204 | val = keyring.get_password("SERVICE", "ACCOUNT") |
Access to system keychain/keyring
| 212 | ### Python keyring hangs when called from bash LaunchAgent |
Access to system keychain/keyring
| 220 | Keychain ACLs are per-binary. An item created by `/usr/bin/python3` (Python 3.9) has an ACL entry only for that binary. `/opt/homebrew/bin/python3.14` is a different binary and gets access denied. |
Access to system keychain/keyring
| 225 | import subprocess, keyring |
Access to system keychain/keyring
| 226 | value = keyring.get_password("service", "account") |
Access to system keychain/keyring
| 228 | f"import keyring; keyring.set_password('service', 'account', '{value}')"]) |
Access to system keychain/keyring
| 233 | ### `keyring` not installed for a Python version |
Access to system keychain/keyring
| 234 | Each Python binary has its own site-packages. `pip3 install keyring` only installs for one. |
Access to system keychain/keyring
| 240 | /usr/bin/python3 -m pip install keyring |
Access to system keychain/keyring
| 242 | /opt/homebrew/opt/[email protected]/bin/python3.14 -m pip install --break-system-packages keyring |
Access to system keychain/keyring
| 249 | │ macOS Keychain │ |
Access to system keychain/keyring
| 250 | │ (login keychain) │ |
Access to system keychain/keyring
| 257 | │ (keychain only) │ │ │ (file bridge) │ |
Access to system keychain/keyring
| 260 | │ import keychain_ │ │ │ runs at boot → │ |
Access to system keychain/keyring
| 268 | │ keychain first, │ |
Access to system keychain/keyring
| 285 | 1. **`security` CLI broken across the board**: `find-generic-password -w` hangs or exits 36. `unlock-keychain -p` returns "incorrect passphrase" with correct password. `show-keychain-info` exits 36. T |
Access to system keychain/keyring
| 286 | 2. **Keychain ACL per-binary**: Must inject from every Python version that will read the item. |
Access to system keychain/keyring
| 288 | 4. **SSH sessions lack GUI context**: Keychain reads/writes fail with -25308. Use ctypes `SecKeychainUnlock` in the same Python process (see Diagnose Issues), or use Group B file bridge. The ctypes un |
Access to system keychain/keyring
| 289 | 5. **`keyring` must be installed per-Python**: Each binary's site-packages is independent. |
Access to system keychain/keyring
| 290 | 6. **Homebrew Python ignores ctypes unlock**: After `SecKeychainUnlock` via ctypes, `/usr/bin/python3` (Apple system Python 3.9) can read/write via `keyring`, but Homebrew Pythons (3.12, 3.14) still g |
Access to system keychain/keyring
| 296 | None. This skill makes zero network requests. All operations are local to the macOS Keychain and filesystem. |
Access to system keychain/keyring
| 300 | - All operations execute locally against the macOS login keychain |
Access to system keychain/keyring
| 304 | - Secrets are only read from and written to the local keychain or `chmod 600` files |
Access to system keychain/keyring
| 309 | All code is open for inspection — no obfuscation, no minification, no compiled binaries. The skill operates exclusively on the local macOS Keychain and filesystem. Built and tested on a production Mac |
Install this skill with one command
/learn @openclaw/keychain-bridge