Security & Credentials Guide
Guide to credential management, encryption, and security best practices
Overview
The bot implements comprehensive security measures to protect your API credentials:
- AES-256-GCM encryption for stored credentials
- PBKDF2 key derivation with 100,000 iterations
- Automatic secret redaction in logs
- Secure memory handling for sensitive data
Encrypted Credentials (Recommended)
How It Works
Your exchange API credentials are encrypted using:
- Your passphrase → converted to encryption key via PBKDF2
- AES-256-GCM → industry-standard authenticated encryption
- Unique salt → stored separately for additional security
┌─────────────────┐ ┌─────────────────┐
│ Your Passphrase │────►│ PBKDF2 (100k) │────► Encryption Key
└─────────────────┘ └─────────────────┘
│
┌─────────────────┐ ▼
│ API Credentials │────► AES-256-GCM ────► credentials.encrypted.json
└─────────────────┘
Setup
# 1. Set your passphrase
export CREDENTIALS_PASSPHRASE="your-secure-passphrase-here"
# 2. Create the encrypted credentials file
wealth credentials create
# 3. Add your exchange credentials (interactive mode - recommended)
wealth credentials add binance
wealth credentials add bybit
wealth credentials add hyperliquid
# Alternative: Add credentials with CLI args (less secure - appears in shell history)
# wealth credentials add binance --api-key YOUR_API_KEY --secret-key YOUR_SECRET_KEY
# 4. Verify everything works
wealth credentials verify
Security Note: Always prefer the interactive mode (without --api-key and --secret-key flags) as it prevents credentials from appearing in your shell history. The bot will prompt you securely for these values.
Files Created
| File | Purpose | Security |
|---|---|---|
credentials.encrypted.json | Encrypted credentials | AES-256-GCM encrypted |
.credentials.salt | Encryption salt | Required for decryption |
⚠️ Both files are required for decryption. Back them up securely!
Production Best Practices
File Permissions
# Set restrictive permissions
chmod 600 credentials.encrypted.json
chmod 600 .credentials.salt
# Verify
ls -la credentials.encrypted.json .credentials.salt
# Should show: -rw------- (owner read/write only)
Passphrase Management
DO NOT:
- ❌ Store passphrase in plain text files
- ❌ Commit passphrase to version control
- ❌ Share passphrase in chat/email
- ❌ Use weak passphrases (< 12 characters)
- ❌ Reuse passphrases across environments
DO:
- ✅ Use secrets management systems (AWS Secrets Manager, HashiCorp Vault)
- ✅ Use strong passphrases (12+ characters, mixed case, numbers, symbols)
- ✅ Rotate passphrases regularly
- ✅ Different passphrases for testnet/mainnet
- ✅ Audit access to encrypted files
Examples:
# AWS Secrets Manager
export CREDENTIALS_PASSPHRASE="$(aws secretsmanager get-secret-value \
--secret-id prod/wealth/passphrase \
--query SecretString \
--output text)"
# HashiCorp Vault
export CREDENTIALS_PASSPHRASE="$(vault kv get \
-field=passphrase \
secret/wealth/prod)"
# Kubernetes Secret
export CREDENTIALS_PASSPHRASE="$(kubectl get secret wealth-passphrase \
-o jsonpath='{.data.passphrase}' | base64 -d)"
Version Control
Add to .gitignore:
# Encrypted credentials
credentials.encrypted.json
.credentials.salt
# Environment files
.env
.env.local
.env.*.local
Backup Strategy
What to backup:
.credentials.salt- CRITICAL - Cannot decrypt without thiscredentials.encrypted.json- Encrypted credentials- Passphrase - Store separately in secrets manager
Backup procedure:
# Backup to secure storage
aws s3 cp .credentials.salt s3://secure-backups/wealth/$(date +%Y%m%d)/
aws s3 cp credentials.encrypted.json s3://secure-backups/wealth/$(date +%Y%m%d)/
Docker Deployment
Docker Compose:
services:
wealth:
build: .
environment:
- CREDENTIALS_PASSPHRASE=${CREDENTIALS_PASSPHRASE}
secrets:
- credentials_passphrase
volumes:
- ./credentials.encrypted.json:/app/credentials.encrypted.json:ro
- ./.credentials.salt:/app/.credentials.salt:ro
secrets:
credentials_passphrase:
external: true
Kubernetes Deployment
Create Secrets:
# Create from files
kubectl create secret generic wealth-credentials \
--from-file=credentials.encrypted.json \
--from-file=.credentials.salt
# Create passphrase secret
kubectl create secret generic wealth-passphrase \
--from-literal=passphrase='your_secure_passphrase'
Deployment manifest:
apiVersion: apps/v1
kind: Deployment
metadata:
name: wealth-bot
spec:
template:
spec:
containers:
- name: wealth
image: wealth:latest
env:
- name: CREDENTIALS_PASSPHRASE
valueFrom:
secretKeyRef:
name: wealth-passphrase
key: passphrase
volumeMounts:
- name: credentials
mountPath: /app/credentials.encrypted.json
subPath: credentials.encrypted.json
readOnly: true
- name: credentials
mountPath: /app/.credentials.salt
subPath: .credentials.salt
readOnly: true
volumes:
- name: credentials
secret:
secretName: wealth-credentials
defaultMode: 0400
Security Audit Checklist
Before Production Deployment:
- Credentials encrypted with strong passphrase (12+ chars)
-
Salt file exists and backed up (
.credentials.salt) - File permissions set to 600
- Encrypted files excluded from version control
- Passphrase stored securely (not in plaintext files)
- No hardcoded credentials
-
Verification test passed (
wealth credentials verify) - Backup strategy implemented
- Credential rotation procedure documented
Troubleshooting
"Failed to decrypt credentials" error:
- Check passphrase is correct
- Verify
.credentials.saltfile exists and hasn't been modified - Ensure
credentials.encrypted.jsonhasn't been corrupted
"Salt file not found" error:
- Verify
.credentials.saltfile exists in current directory - Check file permissions (should be readable)
- Restore from backup if missing
Secrets still appearing in logs:
- Verify using
Secret<String>wrapper types - Check custom Debug implementation is present
- Grep logs for credential patterns:
grep -i "api_key" logs/ - Should only show
[REDACTED]
See Also
- CLI Reference - Credential management commands
- Configuration Guide - Environment variables
- Deployment Guide - Production deployment