Openclaw
Secure key management for AI agents. Use when handling private keys, API secrets, wallet credentials, or when building systems that need agent-controlled funds. Covers secure storage, session keys, le
Secure key management for AI agents. Use when handling private keys, API secrets, wallet credentials, or when building systems that need agent-controlled funds. Covers secure storage, session keys, le
Real data. Real impact.
Emerging
Developers
Per week
Open source
Skills give you superpowers. Install in 30 seconds.
Secure key management patterns for AI agents handling private keys and secrets. Designed to prevent:
op)references/secure-storage.md - 1Password patterns for agent secretsreferences/session-keys.md - ERC-4337 delegated access patternsreferences/leak-prevention.md - Pre-commit hooks and output sanitizationreferences/prompt-injection-defense.md - Input validation and output filtering# Retrieve key at runtime via 1Password PRIVATE_KEY=$(op read "op://Agents/my-agent-wallet/private-key")Use environment injection (key never touches disk)
op run --env-file=.env.tpl -- node agent.js
Use session keys with bounded permissions
(delegate specific capabilities, not full wallet access)
# NEVER store keys in files echo "PRIVATE_KEY=0x123..." > .envNEVER log or print keys
console.log("Key:", privateKey)
NEVER store keys in memory/journal files
Even in "private" agent memory - these can be exfiltrated
NEVER trust unvalidated input near key operations
┌─────────────────────────────────────────────────────┐ │ AI Agent │ ├─────────────────────────────────────────────────────┤ │ Session Key (time/value bounded) │ │ - Expires after N hours │ │ - Spending cap per operation │ │ - Whitelist of allowed contracts │ ├─────────────────────────────────────────────────────┤ │ 1Password / Secret Manager │ │ - Agent retrieves session key at runtime │ │ - Never stores full private key │ │ - Audit log of all accesses │ ├─────────────────────────────────────────────────────┤ │ ERC-4337 Smart Account │ │ - Programmable permissions │ │ - Recovery without private key exposure │ │ - Multi-sig for high-value operations │ ├─────────────────────────────────────────────────────┤ │ Operator (Human) │ │ - Holds master key in hardware wallet │ │ - Issues/revokes session keys │ │ - Monitors agent activity │ └─────────────────────────────────────────────────────┘
# Create dedicated vault (via 1Password app or CLI) op vault create "Agent-Wallets" --description "AI agent wallet credentials"Store agent session key (not master key!)
op item create
--vault "Agent-Wallets"
--category "API Credential"
--title "trading-bot-session"
--field "session-key[password]=0xsession..."
--field "expires=2026-02-15T00:00:00Z"
--field "spending-cap=1000 USDC"
--field "allowed-contracts=0xDEX1,0xDEX2"
import subprocess import jsondef get_session_key(item_name: str) -> dict: """Retrieve session key from 1Password at runtime.""" result = subprocess.run( ["op", "item", "get", item_name, "--vault", "Agent-Wallets", "--format", "json"], capture_output=True, text=True, check=True ) item = json.loads(result.stdout)
# Extract fields fields = {f["label"]: f.get("value") for f in item.get("fields", [])} # Validate session hasn't expired from datetime import datetime expires = datetime.fromisoformat(fields.get("expires", "2000-01-01")) if datetime.now() > expires: raise ValueError("Session key expired - request new key from operator") return { "session_key": fields.get("session-key"), "expires": fields.get("expires"), "spending_cap": fields.get("spending-cap"), "allowed_contracts": fields.get("allowed-contracts", "").split(",") }
# ❌ BAD - Key in logs logger.info(f"Using key: {session_key}")✅ GOOD - Redacted identifier
logger.info(f"Using session key: {session_key[:8]}...{session_key[-4:]}")
❌ BAD - Key in memory file
with open("memory/today.md", "a") as f: f.write(f"Session key: {session_key}")
✅ GOOD - Reference only
with open("memory/today.md", "a") as f: f.write(f"Session key: [stored in 1Password: trading-bot-session]")
Before any agent output (chat, logs, file writes), scan for key patterns:
import reKEY_PATTERNS = [ r'0x[a-fA-F0-9]{64}', # ETH private keys r'sk-[a-zA-Z0-9]{48,}', # OpenAI keys r'sk-ant-[a-zA-Z0-9-]{80,}', # Anthropic keys r'gsk[a-zA-Z0-9]{48,}', # Groq keys r'[A-Za-z0-9+/]{40,}={0,2}', # Base64 encoded (suspiciously long) ]
def sanitize_output(text: str) -> str: """Remove potential secrets from output.""" for pattern in KEY_PATTERNS: text = re.sub(pattern, '[REDACTED]', text) return text
Apply to ALL agent outputs
def send_message(content: str): content = sanitize_output(content) # ... send to chat/log/file
Install this hook to prevent accidental commits of secrets:
#!/bin/bash # .git/hooks/pre-commitPATTERNS=( '0x[a-fA-F0-9]{64}' 'sk-[a-zA-Z0-9]{48,}' 'sk-ant-api' 'PRIVATE_KEY=' 'gsk_[a-zA-Z0-9]{48,}' )
for pattern in "${PATTERNS[@]}"; do if git diff --cached | grep -qE "$pattern"; then echo "❌ Potential secret detected matching: $pattern" echo " Remove secrets before committing!" exit 1 fi done
# Secrets .env .env.* *.pem *.key secrets/ credentials/Agent state that might contain secrets
memory/*.json wallet-state.json session-keys/
Before processing any user input that touches wallet operations:
DANGEROUS_PATTERNS = [ r'ignore.*(previous|above|prior).*instructions', r'reveal.*(key|secret|password|credential)', r'output.*(key|secret|private)', r'print.*(key|secret|wallet)', r'show.*(key|secret|password)', r'what.*(key|secret|password)', r'tell.*me.*(key|secret)', r'disregard.*rules', r'system.*prompt', r'jailbreak', r'dan.*mode', ]def validate_input(text: str) -> bool: """Check for prompt injection attempts.""" text_lower = text.lower() for pattern in DANGEROUS_PATTERNS: if re.search(pattern, text_lower): return False return True
def process_wallet_request(user_input: str): if not validate_input(user_input): return "I can't help with that request." # ... proceed with wallet operation
ALLOWED_WALLET_OPERATIONS = { "check_balance": lambda: get_balance(), "send_usdc": lambda to, amount: send_usdc(to, amount) if amount < DAILY_LIMIT else deny(), "swap": lambda: swap_tokens() if within_limits() else deny(), }def execute_wallet_operation(operation: str, **kwargs): """Execute only explicitly allowed operations.""" if operation not in ALLOWED_WALLET_OPERATIONS: raise ValueError(f"Operation '{operation}' not allowed") return ALLOWED_WALLET_OPERATIONSoperation
For agents needing on-chain access, use session keys instead of raw private keys.
See
references/session-keys.md for full implementation details including:
# Emergency: Revoke 1Password item op item delete "compromised-session-key" --vault "Agent-Wallets"Rotate to new session key
op item create --vault "Agent-Wallets" --category "API Credential"
--title "trading-bot-session-v2" ...
Problem: Agents store keys in
memory/*.md for "persistence"
# memory/2026-02-07.md ## Test Wallet - Private key: 0x9f01dad551039daad3a8c4e43a32035bdd4da54e7b4292268be16e913b0b3e56
Fix: Store reference only:
Private key: [1Password: test-wallet-session]
Problem:
.env.example contains real keys
# .env.example PRIVATE_KEY=sk-ant-api03-real-key-here... # "for testing"
Fix: Use obviously fake placeholders:
PRIVATE_KEY=your-key-here
Problem: Error handling exposes keys
try: sign_transaction(private_key, tx) except Exception as e: logger.error(f"Failed with key {private_key}: {e}") # ❌
Fix: Never include credentials in error context
Problem: Hardcoded test keys make it to main branch
Fix: Use separate test vault, CI checks for key patterns
When running as an OpenClaw agent:
Example TOOLS.md entry:
### Agent Wallet - Address: 0xABC123... - Session key: [1Password: my-agent-session] - Permissions: USDC transfers < 100, approved DEX only - Expires: 2026-02-15 - To rotate: Ask operator via Telegram
No automatic installation available. Please visit the source repository for installation instructions.
View Installation Instructions1,500+ AI skills, agents & workflows. Install in 30 seconds. Part of the Torly.ai family.
© 2026 Torly.ai. All rights reserved.