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

Encryption Flow

flowchart TB
    subgraph Encryption["Credential Encryption Flow"]
        PASS[Your Passphrase] --> PBKDF2[PBKDF2<br/>100k iterations]
        PBKDF2 --> KEY[Encryption Key]
        
        CREDS[API Credentials] --> AES[AES-256-GCM]
        KEY --> AES
        SALT[Unique Salt] --> AES
        
        AES --> FILE[credentials.encrypted.json]
        SALT --> SALTFILE[.credentials.salt]
    end
    
    style PASS fill:#fff3e0
    style KEY fill:#e3f2fd
    style FILE fill:#c8e6c9
    style SALTFILE fill:#c8e6c9

Credential Setup Flow

sequenceDiagram
    participant User
    participant CLI as Wealth CLI
    participant Crypto as Crypto Module
    participant File as Encrypted File
    
    User->>CLI: wealth credentials add binance
    CLI->>User: Prompt for API Key
    User->>CLI: Enter API Key (hidden)
    CLI->>User: Prompt for Secret Key
    User->>CLI: Enter Secret Key (hidden)
    CLI->>Crypto: Encrypt with passphrase
    Crypto->>File: Store encrypted
    CLI->>User: Credentials saved securely

How It Works

Your exchange API credentials are encrypted using:

  1. Your passphrase → converted to encryption key via PBKDF2
  2. AES-256-GCM → industry-standard authenticated encryption
  3. Unique salt → stored separately for additional security
┌─────────────────┐     ┌─────────────────┐
│  Your Passphrase │────►│  PBKDF2 (100k)  │────► Encryption Key
└─────────────────┘     └─────────────────┘
                                │
┌─────────────────┐             ▼
│ API Credentials │────► AES-256-GCM ────► credentials.encrypted.json
└─────────────────┘

Setup

# 1. Create the encrypted credentials file
wealth credentials create

# 2. Add your exchange credentials (interactive mode - recommended)
# You'll be prompted for passphrase if CREDENTIALS_PASSPHRASE is not set
wealth credentials add binance
wealth credentials add bybit
wealth credentials add hyperliquid
wealth credentials add aster

# 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

# 3. Verify everything works
wealth credentials verify

Interactive Passphrase Prompt

When running wealth run or wealth dashboard, the bot will automatically prompt for your passphrase if:

  1. credentials.encrypted.json exists (you've set up encrypted credentials)
  2. CREDENTIALS_PASSPHRASE environment variable is not set
$ wealth dashboard
🔐 Enter credentials passphrase: ********

This allows you to run the bot without storing the passphrase in environment variables or shell history.

Behavior on cancellation: If you press Ctrl+C or enter an empty passphrase, the bot will attempt to fall back to environment-based credentials (legacy mode). If neither method provides valid credentials, the bot will fail with an error.

For automation: Set the CREDENTIALS_PASSPHRASE environment variable to skip the interactive prompt:

export CREDENTIALS_PASSPHRASE="your-secure-passphrase-here"
wealth run  # No prompt, uses env var

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

FilePurposeSecurity
credentials.encrypted.jsonEncrypted credentialsAES-256-GCM encrypted
.credentials.saltEncryption saltRequired 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:

  1. .credentials.salt - CRITICAL - Cannot decrypt without this
  2. credentials.encrypted.json - Encrypted credentials
  3. 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

See Deployment Guide - Docker for complete Docker setup including Docker Compose configuration.

Kubernetes Deployment

See Deployment Guide - Kubernetes for complete K8s manifests and secrets management.

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.salt file exists and hasn't been modified
  • Ensure credentials.encrypted.json hasn't been corrupted

"Salt file not found" error:

  • Verify .credentials.salt file 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