| name | keychain-bridge |
| 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 issues. Use when asked about secrets, keychain, credentials, API keys, or security hardening on macOS. |
| homepage | https://github.com/moltbot/keychain-bridge |
| metadata | {"openclaw":{"emoji":"🔐","requires":{"bins":["bash","python3"],"env":[]},"files":["scripts/*"]},"clawmart":{"price":99,"currency":"USD","category":"security","tags":["keychain","macos","secrets","credentials","tahoe","migration"]}} |
Keychain Bridge
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.
Prerequisites
The keyring Python library must be installed for each Python version that will access secrets:
pip3 install keyring
/usr/bin/python3 -m pip install keyring
/opt/homebrew/opt/python@3.14/bin/python3.14 -m pip install --break-system-packages keyring
Check Keychain Health
Verify the keychain bridge is working correctly:
python3 -c "
import keyring
# Test write
keyring.set_password('keychain-bridge-test', 'test', 'hello')
# Test read
val = keyring.get_password('keychain-bridge-test', 'test')
assert val == 'hello', f'Read back {val!r}, expected hello'
# Cleanup
keyring.delete_password('keychain-bridge-test', 'test')
print('Keychain health: OK')
"
If this fails, see Diagnose Issues below.
Migrate Secrets
Migrate plaintext secret files to macOS Keychain. The migration tool:
- Auto-detects all Python versions on the system
- Injects each secret from ALL detected Python binaries (required for ACL coverage)
- Verifies the round-trip read
- Optionally deletes the original file
python3 SKILL_DIR/scripts/migrate_secrets.py --dir ~/.openclaw/secrets/ --account moltbot --dry-run
python3 SKILL_DIR/scripts/migrate_secrets.py --dir ~/.openclaw/secrets/ --account moltbot
After migration, secrets fall into two groups:
Group A — Keychain Only
Python scripts read directly via keychain_helper.get_secret(service). No file on disk.
Group B — File Bridge
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:
bash SKILL_DIR/scripts/populate_secrets.sh
This reads each Group B secret from keychain and writes it to a chmod 600 file that bash scripts can cat.
Read a Secret
From Python
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from keychain_helper import get_secret
token = get_secret("my-service-name")
The helper tries keychain first, falls back to file read.
From Bash (via Group B file)
MY_SECRET=$(cat ~/.openclaw/secrets/my-service-name)
Ensure the service is listed in populate_secrets.sh so the file is populated at boot.
From Bash (via CLI helper — interactive only)
MY_SECRET=$(python3 path/to/get_secret.py my-service-name)
Write a Secret
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.
PYTHONS=()
[ -x /usr/bin/python3 ] && PYTHONS+=(/usr/bin/python3)
[ -x /opt/homebrew/opt/python@3.14/bin/python3.14 ] && PYTHONS+=(/opt/homebrew/opt/python@3.14/bin/python3.14)
for py in "${PYTHONS[@]}"; do
$py -c "import keyring; keyring.set_password('SERVICE', 'ACCOUNT', 'VALUE')"
done
Or use the migration tool for batch operations.
Audit for Leaks
Check for unexpected plaintext secret files and verify keychain health:
python3 SKILL_DIR/scripts/audit_secrets.py --dir ~/.openclaw/secrets/ --account moltbot
Reports:
- Unexpected files in the secrets directory (potential leaks)
- Keychain items that exist but can't be read (ACL issues)
- Files that exist but aren't in keychain (unmigrated)
- Keychain library installation status per Python version
Diagnose Issues
security find-generic-password -w hangs
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 reads.
Fix: Use Python keyring library instead. It uses the Security framework C API via ctypes, bypassing the broken CLI entirely.
Python keyring returns None or raises errSecInteractionNotAllowed (-25308)
This happens when running from an SSH session. The keychain requires a GUI session (SecurityAgent) context.
Fix: Run from a LaunchAgent (which has GUI session context), or unlock the keychain first:
security unlock-keychain -p "PASSWORD" ~/Library/Keychains/login.keychain-db
Python keyring hangs when called from bash LaunchAgent
Novel finding (macOS Tahoe 26.x). When a bash script is the LaunchAgent program and spawns python3 get_secret.py as a subprocess, the Python process hangs indefinitely. The SecurityAgent session attachment is lost in the bash-to-python subprocess transition.
Fix: Use the Group B file-bridge pattern. Have a Python-native process populate files at boot, then bash scripts read from files.
Alternatively, make Python the direct LaunchAgent program (not a subprocess of bash).
Different Python versions can't read each other's items
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.
Fix: Inject from both Python versions:
import subprocess, keyring
value = keyring.get_password("service", "account")
subprocess.run(["/opt/homebrew/bin/python3.14", "-c",
f"import keyring; keyring.set_password('service', 'account', '{value}')"])
Or use migrate_secrets.py which handles this automatically.
keyring not installed for a Python version
Each Python binary has its own site-packages. pip3 install keyring only installs for one.
pip3 --version
/usr/bin/python3 -m pip install keyring
/opt/homebrew/opt/python@3.14/bin/python3.14 -m pip install --break-system-packages keyring
Architecture Reference
┌─────────────────────┐
│ macOS Keychain │
│ (login keychain) │
└──────────┬──────────┘
│
┌────────────────┼────────────────┐
│ │ │
┌─────────▼─────────┐ │ ┌──────────▼──────────┐
│ Group A │ │ │ Group B │
│ (keychain only) │ │ │ (file bridge) │
│ │ │ │ │
│ Python scripts │ │ │ populate_secrets.sh │
│ import keychain_ │ │ │ runs at boot → │
│ helper.get_secret()│ │ │ writes chmod 600 │
│ │ │ │ files for bash │
└────────────────────┘ │ └──────────────────────┘
│
┌──────────▼──────────┐
│ Fallback │
│ get_secret() tries │
│ keychain first, │
│ then file read │
└─────────────────────┘
When to use Group A
- The consumer is a Python script
- The script runs as a LaunchAgent (or from terminal)
- The script is NOT spawned as a subprocess from a bash LaunchAgent
When to use Group B
- The consumer is a bash script
- The bash script runs as a LaunchAgent
- The secret is referenced by a config file that expects
file:secrets/ paths
Known Issues (macOS Tahoe 26.x)
security CLI broken: find-generic-password -w hangs or exits 36. Use Python keyring instead.
- Keychain ACL per-binary: Must inject from every Python version that will read the item.
- Bash subprocess loses SecurityAgent:
bash LaunchAgent → python3 subprocess hangs. Use Group B file bridge.
- SSH sessions lack GUI context: Keychain reads fail with -25308. Run from LaunchAgent or unlock first.
keyring must be installed per-Python: Each binary's site-packages is independent.
These issues are specific to macOS Tahoe 26.x (macOS 26). Earlier versions (Sonoma 14, Sequoia 15) may not exhibit all of them, but the Group A/B architecture is safe on all versions.
External Endpoints
None. This skill makes zero network requests. All operations are local to the macOS Keychain and filesystem.
Security & Privacy
- All operations execute locally against the macOS login keychain
- No telemetry, analytics, or usage tracking
- No data leaves the machine under any circumstances
- Scripts request no network permissions
- Secrets are only read from and written to the local keychain or
chmod 600 files
- Migration tool never logs or displays secret values
Trust Statement
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 Mini M4 Pro deployment running OpenClaw 24/7 with 12+ API keys and 25 automated scripts.