Awesome-omni-skill credential-manager
MANDATORY security foundation for OpenClaw. Consolidate scattered API keys and credentials into a secure .env file with proper permissions. Includes GPG encryption for high-value secrets, credential rotation tracking, deep scanning, and backup hardening. Use when setting up OpenClaw, migrating credentials, auditing security, or enforcing the .env standard. This is not optional — centralized credential management is a core requirement for secure OpenClaw deployments.
git clone https://github.com/diegosouzapw/awesome-omni-skill
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/testing-security/credential-manager" ~/.claude/skills/diegosouzapw-awesome-omni-skill-credential-manager && rm -rf "$T"
skills/testing-security/credential-manager/SKILL.mdCredential Manager
STATUS: MANDATORY SECURITY FOUNDATION
Consolidate scattered API keys and credentials into a secure, centralized
.env file.
⚠️ This Is Not Optional
Centralized
.env credential management is a core requirement for OpenClaw security. If your credentials are scattered across multiple files, stop and consolidate them now.
THE RULE: All credentials MUST be in
~/.openclaw/.env ONLY. No workspace, no skills, no scripts directories.
See:
- CORE-PRINCIPLE.md - Why this is non-negotiable
- CONSOLIDATION-RULE.md - The single source principle
The Foundation
Every OpenClaw deployment MUST have:
~/.openclaw/.env (mode 600)
This is your single source of truth for all credentials. No exceptions.
Why?
- Single location = easier to secure
- File mode 600 = only you can read
- Git-ignored = won't accidentally commit
- Validated format = catches errors
- Audit trail = know what changed
Scattered credentials = scattered attack surface. This skill fixes that.
What This Skill Does
- Scans for credentials in common locations (including deep scan for hardcoded secrets)
- Backs up existing credential files (timestamped, mode 600)
- Consolidates into
~/.openclaw/.env - Secures with proper permissions (600 files, 700 directories)
- Validates security, format, and entropy
- Encrypts high-value secrets with GPG (wallet keys, private keys, mnemonics)
- Tracks credential rotation schedules
- Enforces best practices via fail-fast checks
- Cleans up old files after migration
Detection Parameters
The skill automatically detects credentials by scanning for:
File Patterns:
— Service config directories~/.config/*/credentials.json
— Nested credential files~/.config/*/*.credentials.json
— Credential files in OpenClaw root~/.openclaw/*.json
— Named credential files (e.g., farcaster-credentials.json)~/.openclaw/*-credentials*
— Memory credential files~/.openclaw/workspace/memory/*-creds.json
— Memory credential files~/.openclaw/workspace/memory/*credentials*.json
— Workspace env files~/.openclaw/workspace/.env
— Subdirectory env files~/.openclaw/workspace/*/.env
— Skill env files~/.openclaw/workspace/skills/*/.env
— Local share directories~/.local/share/*/credentials.json
Sensitive Key Patterns:
- API keys, access tokens, bearer tokens
- Secrets, passwords, passphrases
- OAuth consumer keys
- Private keys, signing keys, wallet keys
- Mnemonics and seed phrases
Deep Scan (--deep flag):
- Greps
,.sh
,.js
,.py
,.mjs
files for hardcoded secrets.ts - Detects high-entropy strings matching common key prefixes (
,sk_
,pk_
,Bearer
+ 64 hex)0x - Excludes
,node_modules/.git/ - Reports file, line number, and key pattern matched
Security Checks:
- File permissions (must be
for files,600
for directories)700 - Backup permissions (must be
for backup files,600
for backup dirs)700 - Git-ignore protection
- Format validation (allows quoted values with spaces)
- Entropy analysis (flags suspiciously low-entropy secrets)
- Private key detection (flags
+ 64 hex char values)0x - Mnemonic detection (flags 12/24 word values)
- Symlink detection (validates symlinked .env targets)
Quick Start
Full Migration (Recommended)
# Scan for credentials ./scripts/scan.py # Deep scan (includes hardcoded secrets in scripts) ./scripts/scan.py --deep # Review and consolidate ./scripts/consolidate.py # Validate security ./scripts/validate.py # Encrypt high-value secrets ./scripts/encrypt.py --keys MAIN_WALLET_PRIVATE_KEY,CUSTODY_PRIVATE_KEY # Check rotation status ./scripts/rotation-check.py
Individual Operations
# Scan only ./scripts/scan.py # Consolidate specific service ./scripts/consolidate.py --service x # Backup without removing ./scripts/consolidate.py --backup-only # Clean up old files ./scripts/cleanup.py --confirm
Common Credential Locations
The skill scans these locations:
~/.config/*/credentials.json ~/.openclaw/*.json ~/.openclaw/*-credentials* ~/.openclaw/workspace/memory/*-creds.json ~/.openclaw/workspace/memory/*credentials*.json ~/.openclaw/workspace/*/.env ~/.openclaw/workspace/skills/*/.env ~/.env (if exists, merges)
Security Features
✅ File permissions: Sets
.env to mode 600 (owner only)
✅ Directory permissions: Sets backup dirs to mode 700 (owner only)
✅ Backup permissions: Sets backup files to mode 600 (owner only)
✅ Git protection: Creates/updates .gitignore
✅ Backups: Timestamped backups before changes (secured)
✅ Validation: Checks format, permissions, entropy, and duplicates
✅ Template: Creates .env.example (safe to share)
✅ GPG encryption: Encrypts high-value secrets at rest
✅ Rotation tracking: Warns when credentials need rotation
✅ Deep scan: Detects hardcoded secrets in source files
✅ Symlink-aware: Validates symlinked .env targets
Output Structure
After migration:
~/.openclaw/ ├── .env # All credentials (secure, mode 600) ├── .env.secrets.gpg # GPG-encrypted high-value keys (mode 600) ├── .env.meta # Rotation metadata (mode 600) ├── .env.example # Template (safe to share) ├── .gitignore # Protects .env and .env.secrets.gpg └── backups/ # (mode 700) └── credentials-old-YYYYMMDD/ # (mode 700) └── *.bak # Backup files (mode 600)
GPG Encryption for High-Value Secrets
Private keys, wallet keys, and mnemonics should never exist as plaintext on disk. Use GPG encryption for these.
Setup GPG
# First-time setup (generates OpenClaw GPG key, configures agent cache) ./scripts/setup-gpg.sh
Encrypt High-Value Keys
# Encrypt specific keys (moves them from .env to .env.secrets.gpg) ./scripts/encrypt.py --keys MAIN_WALLET_PRIVATE_KEY,CUSTODY_PRIVATE_KEY,SIGNER_PRIVATE_KEY # The .env will contain placeholders: # MAIN_WALLET_PRIVATE_KEY=GPG:MAIN_WALLET_PRIVATE_KEY
How Scripts Access Encrypted Keys
The
enforce.py module handles this transparently:
from enforce import get_credential # Works for both plaintext and GPG-encrypted keys key = get_credential('MAIN_WALLET_PRIVATE_KEY') # If value starts with "GPG:", decrypts from .env.secrets.gpg automatically
GPG Agent Caching
On headless servers (VPS), the GPG agent caches the passphrase:
- Default cache TTL: 8 hours
- Configurable via
setup-gpg.sh - Passphrase required once after reboot, then cached
What to Encrypt
| Key Type | Encrypt? | Why |
|---|---|---|
| Wallet private keys | ✅ Yes | Controls funds |
| Custody/signer private keys | ✅ Yes | Controls identity |
| Mnemonics / seed phrases | ✅ Yes | Master recovery |
| API keys (services) | ❌ No | Revocable, low damage |
| Agent IDs, names, URLs | ❌ No | Not secrets |
Credential Rotation Tracking
Setup Rotation Metadata
# Initialize rotation tracking for all keys ./scripts/rotation-check.py --init
Creates
~/.openclaw/.env.meta:
{ "MAIN_WALLET_PRIVATE_KEY": { "created": "2026-01-15", "lastRotated": null, "rotationDays": 90, "risk": "critical" }, "MOLTBOOK_API_KEY": { "created": "2026-02-04", "lastRotated": null, "rotationDays": 180, "risk": "low" } }
Check Rotation Status
# Check which keys need rotation ./scripts/rotation-check.py # Output: # 🔴 MAIN_WALLET_PRIVATE_KEY: 26 days old (critical, rotate every 90 days) # ✅ MOLTBOOK_API_KEY: 7 days old (low, rotate every 180 days)
Rotation Schedules
| Risk Level | Rotation Period | Examples |
|---|---|---|
| Critical | 90 days | Wallet keys, private keys |
| Standard | 180 days | API keys for paid services |
| Low | 365 days | Free-tier API keys, agent IDs |
Add to Heartbeat (Optional)
Add rotation checks to
HEARTBEAT.md for periodic monitoring:
## Credential Rotation (weekly) If 7+ days since last rotation check: 1. Run: ./scripts/rotation-check.py 2. If any keys overdue: notify human 3. Update lastRotationCheck timestamp
Supported Services
Common services auto-detected:
- X (Twitter): OAuth 1.0a credentials
- Farcaster: Custody keys, signer keys, FID credentials
- Molten: Agent intent matching
- Moltbook: Agent social network
- Botchan/4claw: Net Protocol
- OpenAI, Anthropic, Google: AI providers
- GitHub, GitLab: Code hosting
- Coinbase/CDP: Crypto wallet credentials
- Generic:
,API_KEY
,*_TOKEN
patterns*_SECRET
See references/supported-services.md for full list.
Scripts
All scripts support
--help for detailed usage.
scan.py
# Scan and report ./scripts/scan.py # Deep scan (includes hardcoded secrets in scripts) ./scripts/scan.py --deep # Include custom paths ./scripts/scan.py --paths ~/.myapp/config ~/.local/share/creds # JSON output ./scripts/scan.py --format json
consolidate.py
# Interactive mode (prompts before changes) ./scripts/consolidate.py # Auto-confirm (no prompts) ./scripts/consolidate.py --yes # Backup only ./scripts/consolidate.py --backup-only # Specific service ./scripts/consolidate.py --service molten
validate.py
# Full validation (permissions, format, entropy, security) ./scripts/validate.py # Check permissions only ./scripts/validate.py --check permissions # Fix issues automatically ./scripts/validate.py --fix
encrypt.py
# Encrypt specific high-value keys ./scripts/encrypt.py --keys MAIN_WALLET_PRIVATE_KEY,CUSTODY_PRIVATE_KEY # List currently encrypted keys ./scripts/encrypt.py --list # Decrypt (move back to plaintext .env) ./scripts/encrypt.py --decrypt --keys MAIN_WALLET_PRIVATE_KEY
rotation-check.py
# Check rotation status ./scripts/rotation-check.py # Initialize tracking for all keys ./scripts/rotation-check.py --init # Record a rotation ./scripts/rotation-check.py --rotated MOLTBOOK_API_KEY
setup-gpg.sh
# First-time GPG setup for OpenClaw ./scripts/setup-gpg.sh # Configure cache timeout (hours) ./scripts/setup-gpg.sh --cache-hours 12
cleanup.py
# Dry run (shows what would be deleted) ./scripts/cleanup.py # Actually delete old files ./scripts/cleanup.py --confirm # Keep backups ./scripts/cleanup.py --confirm --keep-backups
Migration Workflow
This is the exact step-by-step flow, tested and verified on a live OpenClaw deployment.
Step 1: Scan for Scattered Credentials
cd /path/to/openclaw/skills/credential-manager # Basic scan — finds credential files by path patterns ./scripts/scan.py # Deep scan — also greps source files for hardcoded secrets ./scripts/scan.py --deep
What to look for in output:
- ⚠️ files with mode != 600 (insecure permissions)
- Symlinked
files (should point to main.env
)~/.openclaw/.env - JSON credential files outside
~/.openclaw/.env - Deep scan hits on hardcoded keys in scripts
Example output:
⚠️ /home/user/.openclaw/farcaster-credentials.json Type: json Keys: custodyPrivateKey, signerPrivateKey, ... Mode: 644 ⚠️ Should be 600 for security ✅ /home/user/.openclaw/.env Type: env Keys: API_KEY, X_CONSUMER_KEY, ... Mode: 600
Step 2: Consolidate into .env
./scripts/consolidate.py
Interactive flow:
- Script scans and lists all credential files found
- Backs up existing
to.env~/.openclaw/backups/credentials-old-YYYYMMDD/ - Loads existing
keys.env - Processes each credential file:
- Auto-detects service (x, farcaster, moltbook, molten, etc.)
- Normalizes key names (e.g.,
→custodyPrivateKey
)FARCASTER_CUSTODY_PRIVATE_KEY - Shows mapping:
key → ENV_KEY
- Asks for confirmation:
Proceed? [y/N] - Writes merged
(mode 600).env - Creates
template (safe to share).env.example - Updates
.gitignore
For credentials not auto-detected (e.g., nested JSON like
farcaster-credentials.json with multiple accounts), manually add to .env:
cat >> ~/.openclaw/.env << 'EOF' # FARCASTER (Active: mr-teeclaw, FID 2700953) FARCASTER_FID=2700953 FARCASTER_FNAME=mr-teeclaw FARCASTER_CUSTODY_ADDRESS=0x... FARCASTER_CUSTODY_PRIVATE_KEY=0x... FARCASTER_SIGNER_PUBLIC_KEY=... FARCASTER_SIGNER_PRIVATE_KEY=... # FARCASTER LEGACY (teeclaw, FID 2684290) FARCASTER_LEGACY_FID=2684290 FARCASTER_LEGACY_CUSTODY_ADDRESS=0x... FARCASTER_LEGACY_CUSTODY_PRIVATE_KEY=0x... FARCASTER_LEGACY_SIGNER_PUBLIC_KEY=... FARCASTER_LEGACY_SIGNER_PRIVATE_KEY=... EOF chmod 600 ~/.openclaw/.env
Step 3: Validate
./scripts/validate.py
Checks performed:
- ✅
permissions (must be 600).env - ✅
coverage.gitignore - ✅ Format validation (key format, quoting, duplicates)
- ✅ Security analysis:
- Detects plaintext private keys (
+ 64 hex chars) → recommends GPG0x - Detects mnemonic/seed phrases (12/24 word values) → recommends GPG
- Entropy analysis on SECRET/PRIVATE_KEY/PASSWORD fields
- Flags weak/placeholder values
- Detects plaintext private keys (
- ✅ Backup permissions (files must be 600, directories 700)
Fix issues automatically:
./scripts/validate.py --fix
This fixes: file permissions, directory permissions, backup permissions, gitignore. It does NOT auto-fix format issues or encrypt keys — those require manual action.
Step 4: Setup GPG and Encrypt Private Keys
# First-time GPG setup (configures agent cache, tests encrypt/decrypt) ./scripts/setup-gpg.sh # Optional: --cache-hours 12 (default: 8)
Encrypt high-value keys:
# Encrypt wallet + Farcaster private keys ./scripts/encrypt.py --keys MAIN_WALLET_PRIVATE_KEY,FARCASTER_CUSTODY_PRIVATE_KEY,FARCASTER_SIGNER_PRIVATE_KEY,FARCASTER_LEGACY_CUSTODY_PRIVATE_KEY,FARCASTER_LEGACY_SIGNER_PRIVATE_KEY
What happens:
- Prompts for a GPG passphrase (or reads
env var)OPENCLAW_GPG_PASSPHRASE - Extracts specified key values from
.env - Stores them encrypted in
(AES256, mode 600)~/.openclaw/.env.secrets.gpg - Replaces
values with.env
placeholdersGPG:KEY_NAME - Scripts using
orget_credential()
decrypt transparently_load_cred()
Save passphrase to .env for automated decryption:
echo 'OPENCLAW_GPG_PASSPHRASE=your-passphrase-here' >> ~/.openclaw/.env chmod 600 ~/.openclaw/.env
Verify encryption:
# Check .env has GPG placeholders grep "GPG:" ~/.openclaw/.env # List all encrypted keys ./scripts/encrypt.py --list
Step 5: Initialize Rotation Tracking
./scripts/rotation-check.py --init
Auto-classifies all keys by risk:
- Critical (90-day rotation):
,*PRIVATE_KEY
,*MNEMONIC
,*SEED
,*WALLET_KEY
,*CUSTODY**SIGNER* - Standard (180-day rotation):
,*API_KEY
,*SECRET
,*TOKEN
,*BEARER
,*CONSUMER**ACCESS* - Low (365-day rotation): Everything else
Creates
~/.openclaw/.env.meta (mode 600) with creation dates and rotation schedules.
Check rotation status anytime:
./scripts/rotation-check.py
Step 6: Cleanup Old Credential Files
# Dry run first — see what would be deleted ./scripts/cleanup.py # Actually delete (prompts for 'DELETE' confirmation) ./scripts/cleanup.py --confirm
Also manually remove migrated files not caught by the scanner:
# Example: farcaster-credentials.json was manually migrated cp ~/.openclaw/farcaster-credentials.json ~/.openclaw/backups/credentials-old-YYYYMMDD/farcaster-credentials.json.bak chmod 600 ~/.openclaw/backups/credentials-old-YYYYMMDD/farcaster-credentials.json.bak rm ~/.openclaw/farcaster-credentials.json
Step 7: Update Scripts That Referenced Old Files
Any scripts that loaded from JSON credential files or hardcoded paths need updating.
Pattern — Bash scripts:
# OLD (insecure): FARCASTER_CREDS="/home/user/.openclaw/farcaster-credentials.json" fid=$(jq -r '.fid' "$FARCASTER_CREDS") private_key=$(jq -r '.custodyPrivateKey' "$FARCASTER_CREDS") # NEW (secure, GPG-aware): ENV_FILE="$HOME/.openclaw/.env" _load_cred() { local key="$1" local value value=$(grep "^${key}=" "$ENV_FILE" | head -1 | cut -d= -f2-) if [[ "$value" == GPG:* ]]; then local gpg_key="${value#GPG:}" local passphrase="${OPENCLAW_GPG_PASSPHRASE:-}" if [ -n "$passphrase" ]; then value=$(echo "$passphrase" | gpg -d --batch --quiet --passphrase-fd 0 "$HOME/.openclaw/.env.secrets.gpg" | python3 -c "import json,sys; print(json.load(sys.stdin).get('$gpg_key',''))") else value=$(gpg -d --batch --quiet "$HOME/.openclaw/.env.secrets.gpg" | python3 -c "import json,sys; print(json.load(sys.stdin).get('$gpg_key',''))") fi fi echo "$value" } fid=$(_load_cred "FARCASTER_FID") private_key=$(_load_cred "FARCASTER_CUSTODY_PRIVATE_KEY")
Pattern — Node.js scripts:
// OLD (insecure): const creds = JSON.parse(fs.readFileSync('~/.openclaw/farcaster-credentials.json')); const privateKey = creds.custodyPrivateKey; // NEW (secure, GPG-aware): const ENV_PATH = path.join(os.homedir(), '.openclaw/.env'); const SECRETS_PATH = path.join(os.homedir(), '.openclaw/.env.secrets.gpg'); function loadCred(key) { const content = fs.readFileSync(ENV_PATH, 'utf8'); for (const line of content.split('\n')) { if (line.startsWith(key + '=')) { let value = line.slice(key.length + 1).trim(); if (value.startsWith('GPG:')) { const { execSync } = require('child_process'); const passphrase = process.env.OPENCLAW_GPG_PASSPHRASE || ''; const cmd = passphrase ? `echo "${passphrase}" | gpg -d --batch --quiet --passphrase-fd 0 "${SECRETS_PATH}"` : `gpg -d --batch --quiet "${SECRETS_PATH}"`; const secrets = JSON.parse(execSync(cmd, { encoding: 'utf8' })); return secrets[value.slice(4)] || ''; } return value; } } return ''; } const privateKey = loadCred('FARCASTER_CUSTODY_PRIVATE_KEY');
Pattern — Python scripts:
# Use the enforce module (recommended): import sys from pathlib import Path sys.path.insert(0, str(Path.home() / 'openclaw/skills/credential-manager/scripts')) from enforce import get_credential private_key = get_credential('FARCASTER_CUSTODY_PRIVATE_KEY') # Auto-decrypts GPG
Step 8: Final Validation
# Run full validation — should show all green ./scripts/validate.py # Verify encrypted keys ./scripts/encrypt.py --list # Check rotation status ./scripts/rotation-check.py # Test a script that uses credentials bash /path/to/your/script.sh
Expected final state:
~/.openclaw/ ├── .env # All credentials (mode 600, private keys = GPG:*) ├── .env.secrets.gpg # GPG-encrypted private keys (mode 600) ├── .env.meta # Rotation tracking metadata (mode 600) ├── .env.example # Template (safe to share) ├── .gitignore # Protects .env, .env.secrets.gpg, .env.meta └── backups/ # (mode 700) └── credentials-old-YYYYMMDD/ # (mode 700) └── *.bak # Backup files (mode 600)
For Skill Developers: Enforce This Standard
Other OpenClaw skills MUST validate credentials are secure before using them:
Python Skills
#!/usr/bin/env python3 import sys from pathlib import Path # Add credential-manager scripts to path sys.path.insert(0, str(Path.home() / '.openclaw/skills/credential-manager/scripts')) # Enforce secure .env (exits if not compliant) from enforce import require_secure_env, get_credential require_secure_env() # Now safe to load credentials (handles GPG-encrypted keys transparently) api_key = get_credential('SERVICE_API_KEY') wallet_key = get_credential('MAIN_WALLET_PRIVATE_KEY') # Auto-decrypts from GPG
Bash Skills
#!/usr/bin/env bash set -euo pipefail # Validate .env exists and is secure if ! python3 ~/.openclaw/skills/credential-manager/scripts/enforce.py; then exit 1 fi # Now safe to load source ~/.openclaw/.env
This creates a fail-fast system: If credentials aren't properly secured, skills refuse to run. Users are forced to fix it.
Loading Credentials
After migration, load from
.env:
Python
import os from pathlib import Path # Load .env env_file = Path.home() / '.openclaw' / '.env' with open(env_file) as f: for line in f: if '=' in line and not line.strip().startswith('#'): key, val = line.strip().split('=', 1) os.environ[key] = val # Use credentials api_key = os.getenv('SERVICE_API_KEY')
Bash
# Load .env set -a source ~/.openclaw/.env set +a # Use credentials echo "$SERVICE_API_KEY"
Using Existing Loaders
If you migrated using OpenClaw scripts:
from load_credentials import get_credentials creds = get_credentials('x')
Adding New Credentials
Edit
~/.openclaw/.env:
# Add new service NEW_SERVICE_API_KEY=your_key_here NEW_SERVICE_SECRET=your_secret_here
Update template too:
# Edit .env.example NEW_SERVICE_API_KEY=your_key_here NEW_SERVICE_SECRET=your_secret_here
If the new credential is high-value (private key, wallet key):
# Add to .env first, then encrypt ./scripts/encrypt.py --keys NEW_SERVICE_PRIVATE_KEY
Security Best Practices
See references/security.md for detailed security guidelines.
Quick checklist:
- ✅
has 600 permissions.env - ✅
is git-ignored.env - ✅ Backup files have 600 permissions
- ✅ Backup directories have 700 permissions
- ✅ No credentials in code or logs (use
scan to verify)--deep - ✅ Private keys encrypted with GPG
- ✅ Rotation schedule established and tracked
- ✅ Symlinked .env files point to the main .env only
- ✅ No credentials in shell history (use
, notsource
)export KEY=val
Rollback
If something goes wrong:
# Find your backup ls -la ~/.openclaw/backups/ # Restore specific file cp ~/.openclaw/backups/credentials-old-YYYYMMDD/x-credentials.json.bak \ ~/.config/x/credentials.json # Decrypt GPG secrets back to plaintext ./scripts/encrypt.py --decrypt --keys MAIN_WALLET_PRIVATE_KEY
Notes
- Non-destructive by default: Original files backed up before removal
- Idempotent: Safe to run multiple times
- Extensible: Add custom credential patterns in scripts
- Secure: Never logs full credentials, only metadata
- GPG-aware: Transparently handles encrypted and plaintext credentials
- Backup-hardened: All backups secured with proper permissions
- Symlink-aware: Detects and validates symlinked credential files