OpenClaw Credential Manager
by @teeclaw
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.
clawhub install openclaw-credential-managerπ About This Skill
name: credential-manager description: 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.
Credential 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:
The Foundation
Every OpenClaw deployment MUST have:
~/.openclaw/.env (mode 600)
This is your single source of truth for all credentials. No exceptions.
Why?
Scattered credentials = scattered attack surface. This skill fixes that.
What This Skill Does
1. Scans for credentials in common locations (including deep scan for hardcoded secrets)
2. Backs up existing credential files (timestamped, mode 600)
3. Consolidates into ~/.openclaw/.env
4. Secures with proper permissions (600 files, 700 directories)
5. Validates security, format, and entropy
6. Encrypts high-value secrets with GPG (wallet keys, private keys, mnemonics)
7. Tracks credential rotation schedules
8. Enforces best practices via fail-fast checks
9. Cleans up old files after migration
Detection Parameters
The skill automatically detects credentials by scanning for:
File Patterns:
~/.config/*/credentials.json β Service config directories~/.config/*/*.credentials.json β Nested credential files~/.openclaw/*.json β Credential files in OpenClaw root~/.openclaw/*-credentials* β Named credential files (e.g., farcaster-credentials.json)~/.openclaw/workspace/memory/*-creds.json β Memory credential files~/.openclaw/workspace/memory/*credentials*.json β Memory credential files~/.openclaw/workspace/.env β Workspace env files~/.openclaw/workspace/*/.env β Subdirectory env files~/.openclaw/workspace/skills/*/.env β Skill env files~/.local/share/*/credentials.json β Local share directoriesSensitive Key Patterns:
Deep Scan (--deep flag):
.sh, .js, .py, .mjs, .ts files for hardcoded secretssk_, pk_, Bearer, 0x + 64 hex)node_modules/, .git/Security Checks:
600 for files, 700 for directories)600 for backup files, 700 for backup dirs)0x + 64 hex char values)Quick Start
Full Migration (Recommended)
# Scan for credentials
./scripts/scan.pyDeep scan (includes hardcoded secrets in scripts)
./scripts/scan.py --deepReview and consolidate
./scripts/consolidate.pyValidate security
./scripts/validate.pyEncrypt high-value secrets
./scripts/encrypt.py --keys MAIN_WALLET_PRIVATE_KEY,CUSTODY_PRIVATE_KEYCheck rotation status
./scripts/rotation-check.py
Individual Operations
# Scan only
./scripts/scan.pyConsolidate specific service
./scripts/consolidate.py --service xBackup without removing
./scripts/consolidate.py --backup-onlyClean 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_KEYThe .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_credentialWorks 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:
setup-gpg.shWhat 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.pyOutput:
π΄ 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:
API_KEY, *_TOKEN, *_SECRET patternsSee references/supported-services.md for full list.
Scripts
All scripts support --help for detailed usage.
scan.py
# Scan and report
./scripts/scan.pyDeep scan (includes hardcoded secrets in scripts)
./scripts/scan.py --deepInclude custom paths
./scripts/scan.py --paths ~/.myapp/config ~/.local/share/credsJSON output
./scripts/scan.py --format json
consolidate.py
# Interactive mode (prompts before changes)
./scripts/consolidate.pyAuto-confirm (no prompts)
./scripts/consolidate.py --yesBackup only
./scripts/consolidate.py --backup-onlySpecific service
./scripts/consolidate.py --service molten
validate.py
# Full validation (permissions, format, entropy, security)
./scripts/validate.pyCheck permissions only
./scripts/validate.py --check permissionsFix issues automatically
./scripts/validate.py --fix
encrypt.py
# Encrypt specific high-value keys
./scripts/encrypt.py --keys MAIN_WALLET_PRIVATE_KEY,CUSTODY_PRIVATE_KEYList currently encrypted keys
./scripts/encrypt.py --listDecrypt (move back to plaintext .env)
./scripts/encrypt.py --decrypt --keys MAIN_WALLET_PRIVATE_KEY
rotation-check.py
# Check rotation status
./scripts/rotation-check.pyInitialize tracking for all keys
./scripts/rotation-check.py --initRecord a rotation
./scripts/rotation-check.py --rotated MOLTBOOK_API_KEY
setup-gpg.sh
# First-time GPG setup for OpenClaw
./scripts/setup-gpg.shConfigure cache timeout (hours)
./scripts/setup-gpg.sh --cache-hours 12
cleanup.py
# Dry run (shows what would be deleted)
./scripts/cleanup.pyActually delete old files
./scripts/cleanup.py --confirmKeep 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-managerBasic scan β finds credential files by path patterns
./scripts/scan.pyDeep scan β also greps source files for hardcoded secrets
./scripts/scan.py --deep
What to look for in output:
.env files (should point to main ~/.openclaw/.env)~/.openclaw/.envExample 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:
1. Script scans and lists all credential files found
2. Backs up existing .env to ~/.openclaw/backups/credentials-old-YYYYMMDD/
3. Loads existing .env keys
4. 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
5. Asks for confirmation: Proceed? [y/N]
6. Writes merged .env (mode 600)
7. Creates .env.example template (safe to share)
8. 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=...
EOFchmod 600 ~/.openclaw/.env
Step 3: Validate
./scripts/validate.py
Checks performed:
.env permissions (must be 600).gitignore coverage0x + 64 hex chars) β recommends GPG
- Detects mnemonic/seed phrases (12/24 word values) β recommends GPG
- Entropy analysis on SECRET/PRIVATE_KEY/PASSWORD fields
- Flags weak/placeholder values
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:
1. Prompts for a GPG passphrase (or reads OPENCLAW_GPG_PASSPHRASE env var)
2. Extracts specified key values from .env
3. Stores them encrypted in ~/.openclaw/.env.secrets.gpg (AES256, mode 600)
4. Replaces .env values with GPG:KEY_NAME placeholders
5. Scripts using get_credential() or _load_cred() decrypt transparently
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/.envList all encrypted keys
./scripts/encrypt.py --list
Step 5: Initialize Rotation Tracking
./scripts/rotation-check.py --init
Auto-classifies all keys by risk:
*PRIVATE_KEY, *MNEMONIC, *SEED, *WALLET_KEY, *CUSTODY*, *SIGNER**API_KEY, *SECRET, *TOKEN, *BEARER, *CONSUMER*, *ACCESS*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.pyActually 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_credentialprivate_key = get_credential('FARCASTER_CUSTODY_PRIVATE_KEY') # Auto-decrypts GPG
Step 8: Final Validation
# Run full validation β should show all green
./scripts/validate.pyVerify encrypted keys
./scripts/encrypt.py --listCheck rotation status
./scripts/rotation-check.pyTest 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 PathAdd 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_credentialrequire_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 pipefailValidate .env exists and is secure
if ! python3 ~/.openclaw/skills/credential-manager/scripts/enforce.py; then
exit 1
fiNow 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 PathLoad .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] = valUse credentials
api_key = os.getenv('SERVICE_API_KEY')
Bash
# Load .env
set -a
source ~/.openclaw/.env
set +aUse 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:
.env has 600 permissions.env is git-ignored--deep scan to verify)source, not 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.jsonDecrypt GPG secrets back to plaintext
./scripts/encrypt.py --decrypt --keys MAIN_WALLET_PRIVATE_KEY
Notes
π‘ Examples
Full Migration (Recommended)
# Scan for credentials
./scripts/scan.pyDeep scan (includes hardcoded secrets in scripts)
./scripts/scan.py --deepReview and consolidate
./scripts/consolidate.pyValidate security
./scripts/validate.pyEncrypt high-value secrets
./scripts/encrypt.py --keys MAIN_WALLET_PRIVATE_KEY,CUSTODY_PRIVATE_KEYCheck rotation status
./scripts/rotation-check.py
Individual Operations
# Scan only
./scripts/scan.pyConsolidate specific service
./scripts/consolidate.py --service xBackup without removing
./scripts/consolidate.py --backup-onlyClean up old files
./scripts/cleanup.py --confirm