keychain-manager
Manages secrets and API keys in the macOS Keychain using the security CLI. Triggered when a user asks to store, retrieve, list, rotate, or delete tokens and credentials. Uses a consistent naming convention with the bbmisa account and uppercase service names. Never exposes secrets in plaintext output or commits them to git.
Keychain Manager
Store, retrieve, list, rotate, and delete secrets in the macOS Keychain via the security CLI.
When to Trigger
- User asks to store a secret, API key, or token
- User asks to retrieve a credential from the keychain
- User wants to manage, rotate, or delete stored keys
- User asks "add to keychain" or "get from keychain"
- A workflow requires a secret that should not be hardcoded
Naming Convention
All entries use a consistent format:
- Account:
bbmisa(always-a bbmisa) - Service: UPPERCASE_WITH_UNDERSCORES (e.g.,
GITHUB_PERSONAL_ACCESS_TOKEN)
This ensures secrets are easily discoverable and follow a predictable pattern.
Operations
Store a Secret
security add-generic-password -a bbmisa -s SERVICE_NAME -w "token_value"
To update an existing entry (overwrite):
security add-generic-password -a bbmisa -s SERVICE_NAME -w "new_token_value" -U
The -U flag updates the password if the entry already exists.
Retrieve a Secret
security find-generic-password -a bbmisa -s SERVICE_NAME -w
Returns only the password value to stdout. Use in scripts:
TOKEN=$(security find-generic-password -a bbmisa -s SERVICE_NAME -w 2>/dev/null)
List Stored Secrets
Search by pattern:
security dump-keychain | grep "svce" | grep -i "pattern"
List all Berna-managed secrets:
security dump-keychain | grep -A4 '"acct"<blob>="bbmisa"' | grep "svce"
Delete a Secret
security delete-generic-password -a bbmisa -s SERVICE_NAME
Rotate a Secret
Follow this sequence — never delete before verifying the new value:
- Retrieve the old value (for rollback if needed)
- Store the new value with
-U - Verify retrieval returns the new value
- Confirm with the user
# Step 1: Note old value exists
security find-generic-password -a bbmisa -s SERVICE_NAME -w > /dev/null 2>&1 && echo "Old value exists"
# Step 2: Update with new value
security add-generic-password -a bbmisa -s SERVICE_NAME -w "new_token_value" -U
# Step 3: Verify
security find-generic-password -a bbmisa -s SERVICE_NAME -w
Shell Integration
Add to ~/.zshrc for automatic environment variable export:
export GITHUB_TOKEN="$(security find-generic-password -a bbmisa -s GITHUB_PERSONAL_ACCESS_TOKEN -w 2>/dev/null)"
export HUGGINGFACE_TOKEN="$(security find-generic-password -a bbmisa -s HUGGINGFACE_TOKEN -w 2>/dev/null)"
export FIRECRAWL_API_KEY="$(security find-generic-password -a bbmisa -s FIRECRAWL_API_KEY -w 2>/dev/null)"
The 2>/dev/null suppresses errors if the key does not exist, preventing shell startup noise.
Known Tokens in Keychain
These tokens are already stored and available:
| Service Name | Used For |
|---|---|
HUGGINGFACE_TOKEN | HuggingFace Inference API |
FIRECRAWL_API_KEY | Firecrawl web scraping |
MEM0_API_KEY | Mem0 persistent AI memory |
DAYTONA_API_KEY | Daytona dev environments |
GITHUB_PERSONAL_ACCESS_TOKEN | GitHub API and CLI auth |
Anti-Patterns
Never do these:
- Echo tokens to stdout unmasked — if displaying for verification, show only first 4 and last 4 characters
- Store in plaintext files — no
.envfiles with real values committed to git; use.env.examplewith placeholders - Commit keychain retrieval output — never let
security find-generic-passwordoutput end up in git history - Use short or ambiguous service names — always use descriptive uppercase names (e.g.,
STRIPE_LIVE_SECRET_KEYnotstripe) - Skip the
-a bbmisaflag — without it, entries are harder to find and may collide with system entries
Masked Display Helper
When the user asks to verify a stored secret, display it masked:
TOKEN=$(security find-generic-password -a bbmisa -s SERVICE_NAME -w 2>/dev/null)
echo "${TOKEN:0:4}****${TOKEN: -4}"
This shows ghp_****Ab1x — enough to confirm identity without exposing the full value.