The Rule
NEVER let secrets appear in session logs. This means:
- No
cat ~/.secrets/* - No
echo $API_KEY - No
curl -H "Authorization: Bearer sk-actual-key" - No reading secret files with the Read tool
ALWAYS use self-destructing scripts that keep secrets internal.
Where Secrets Live
~/.secrets/
├── openai.env # OPENAI_API_KEY=sk-...
├── jira.json # {"apiToken": "...", "email": "...", "domain": "..."}
├── cloudflare.ini # [cloudflare]\napi_token=...
├── github.env # GH_TOKEN=ghp_...
└── aws.env # AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=...
Files can be any format (.env, .json, .ini, .yaml). You figure out how to parse them.
The Pattern
When you need to use secrets, write a self-destructing script:
cat > /tmp/vault-XXXXXX.sh << 'EOF'
#!/bin/bash
set +x # never trace
# 1. Read secrets from file (parse whatever format)
API_KEY=$(jq -r '.apiToken' ~/.secrets/jira.json)
EMAIL=$(jq -r '.email' ~/.secrets/jira.json)
# 2. Use them in your command
curl -s -X GET \
-H "Authorization: Basic $(echo -n "$EMAIL:$API_KEY" | base64)" \
"https://mycompany.atlassian.net/rest/api/3/search?jql=..."
# 3. Self-destruct
rm -f "$0"
EOF
chmod 700 /tmp/vault-XXXXXX.sh
/tmp/vault-XXXXXX.sh
What gets logged: The script creation and output. What stays secret: The actual API key values (only exist inside the running script).
Safety Rules
These prevent classes of bugs where secrets leak through error messages.
- Suppress stderr on source lines:
source ~/.secrets/foo.env 2>/dev/null- If the env file has a syntax error, bash prints the line (with your secret) in the error message
- Never put secrets into bash variable names with special characters. If a tool needs tokens in a config file format (like
.npmrc,.netrc,.pypirc), write a temp config file instead:# WRONG - bash can't parse // in variable names, error message leaks the token NPM_CONFIG_//registry.npmjs.org/:_authToken="$NPM_TOKEN" # RIGHT - write to temp config file, token stays in-process echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > /tmp/.npmrc-vault npm publish --userconfig /tmp/.npmrc-vault rm -f /tmp/.npmrc-vault - Unset secrets after use:
unset API_KEY- reduces the window where a subsequent error could leak them - Never use
set -xinside vault scripts - it traces every line with expanded variables - Run the script with stderr suppressed if needed:
./vault-$$.sh 2>&1ensures all output comes through stdout (which you control)
Helper Commands
These are optional - you can always write scripts directly.
# List what's in ~/.secrets/
/secrets list
# Check if a service has secrets configured
/secrets test <service>
# Scan session logs for leaked secrets
/secrets scan
/secrets scan -v # Verbose
# Redact leaked secrets from session logs
/secrets redact
/secrets redact --dry-run # Preview
# Security audit (permissions, leaks, etc.)
/secrets audit
# Auto-fix permission issues
/secrets fix
Examples
OpenAI API call
cat > /tmp/vault-$$.sh << 'EOF'
#!/bin/bash
source ~/.secrets/openai.env 2>/dev/null
curl -s https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4","messages":[{"role":"user","content":"hello"}]}'
rm -f "$0"
EOF
chmod 700 /tmp/vault-$$.sh && /tmp/vault-$$.sh
Jira with JSON config
cat > /tmp/vault-$$.sh << 'EOF'
#!/bin/bash
DOMAIN=$(jq -r '.domain' ~/.secrets/jira.json 2>/dev/null)
EMAIL=$(jq -r '.email' ~/.secrets/jira.json 2>/dev/null)
TOKEN=$(jq -r '.apiToken' ~/.secrets/jira.json 2>/dev/null)
AUTH=$(printf "%s:%s" "$EMAIL" "$TOKEN" | base64 -w0)
curl -s "https://${DOMAIN}/rest/api/3/myself" \
-H "Authorization: Basic $AUTH" \
-H "Content-Type: application/json"
rm -f "$0"
EOF
chmod 700 /tmp/vault-$$.sh && /tmp/vault-$$.sh
AWS CLI
cat > /tmp/vault-$$.sh << 'EOF'
#!/bin/bash
source ~/.secrets/aws.env 2>/dev/null
aws s3 ls
rm -f "$0"
EOF
chmod 700 /tmp/vault-$$.sh && /tmp/vault-$$.sh
GitHub CLI
cat > /tmp/vault-$$.sh << 'EOF'
#!/bin/bash
source ~/.secrets/github.env 2>/dev/null
gh api /user
rm -f "$0"
EOF
chmod 700 /tmp/vault-$$.sh && /tmp/vault-$$.sh
npm publish (config file pattern)
cat > /tmp/vault-$$.sh << 'EOF'
#!/bin/bash
source ~/.secrets/npm.env 2>/dev/null
# Write token to temp .npmrc (never use env var names with special chars)
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > /tmp/.npmrc-vault
unset NPM_TOKEN
npm publish --access public --userconfig /tmp/.npmrc-vault
# Clean up
rm -f /tmp/.npmrc-vault
rm -f "$0"
EOF
chmod 700 /tmp/vault-$$.sh && /tmp/vault-$$.sh
Key Points
- Secrets stay inside the script - never echoed, never returned to Claude
- Script self-destructs -
rm -f "$0"at the end - Only output is the API response - that's what Claude sees
- Any file format works - parse with jq, source, grep, whatever fits
- chmod 700 - script is only readable/executable by user
- Suppress stderr on source lines -
2>/dev/nullprevents error messages from leaking secrets - Use temp config files for tools that need tokens in config format (.npmrc, .netrc, .pypirc)
Remediation
Already have leaked secrets in session logs?
/secrets scan # Find them
/secrets redact # Remove them (no backup - that would preserve the leak!)
Detected patterns: OpenAI (sk-), Anthropic (sk-ant-), GitHub (ghp_), AWS (AKIA), Stripe, Slack, Discord, and generic Bearer/password patterns.
