skills/bagman/SKILL.md
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, leak prevention, prompt injection defense, and MetaMask Delegation Framework integration.
npx skillsauth add profbernardoj/baseclaw.ai bagmanInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Secure key management patterns for AI agents handling wallets, private keys, and secrets.
# Install 1Password CLI
brew install 1password-cli
# Authenticate
eval $(op signin)
# Create vault for agent credentials
op vault create "Agent-Credentials"
# Run examples
cd examples && python test_suite.py
| Rule | Why | |------|-----| | Never store raw private keys | Config, env, memory, or conversation = leaked | | Use delegated access | Session keys with time/value/scope limits | | Secrets via secret manager | 1Password, Vault, AWS Secrets Manager | | Sanitize all outputs | Scan for key patterns before any response | | Validate all inputs | Check for injection attempts before wallet ops |
┌─────────────────────────────────────────────────────┐
│ AI Agent │
├─────────────────────────────────────────────────────┤
│ Session Key (bounded) │
│ ├─ Expires after N hours │
│ ├─ Max spend per tx/day │
│ └─ Whitelist of allowed contracts/methods │
├─────────────────────────────────────────────────────┤
│ Secret Manager (1Password/Vault) │
│ ├─ Retrieve at runtime only │
│ ├─ Never persist to disk │
│ └─ Audit trail of accesses │
├─────────────────────────────────────────────────────┤
│ Smart Account (ERC-4337) │
│ ├─ Programmable permissions │
│ └─ Recovery without key exposure │
└─────────────────────────────────────────────────────┘
| File | Purpose |
|------|---------|
| examples/secret_manager.py | 1Password integration for runtime secret retrieval |
| examples/sanitizer.py | Output sanitization (keys, seeds, tokens) |
| examples/validator.py | Input validation (prompt injection defense) |
| examples/session_keys.py | ERC-4337 session key configuration |
| examples/delegation_integration.ts | MetaMask Delegation Framework (EIP-7710) |
| examples/pre-commit | Git hook to block secret commits |
| examples/test_suite.py | Adversarial test suite |
| docs/prompt-injection.md | Deep dive on injection defense |
| docs/secure-storage.md | Secret storage patterns |
| docs/session-keys.md | Session key architecture |
| docs/leak-prevention.md | Output sanitization patterns |
| docs/delegation-framework.md | On-chain permission enforcement (EIP-7710) |
# Retrieve at runtime (never store result)
SESSION_KEY=$(op read "op://Agents/my-agent/session-key")
# Run with injected secrets (never touch disk)
op run --env-file=.env.tpl -- python agent.py
PRIVATE_KEY=op://Agents/trading-bot/session-key
RPC_URL=op://Infra/alchemy/sepolia-url
OPENAI_API_KEY=op://Services/openai/api-key
from secret_manager import get_session_key
# Retrieve validated session key
creds = get_session_key("trading-bot-session")
# Check validity
if creds.is_expired():
raise ValueError("Session expired - request renewal from operator")
print(f"Time remaining: {creds.time_remaining()}")
print(f"Allowed contracts: {creds.allowed_contracts}")
# Use the key (never log it!)
client.set_signer(creds.session_key)
Configure 1Password vault permissions:
Agent-Credentials/
├── trading-bot-session # Agent can read
├── payment-bot-session # Agent can read
└── master-key # Operator ONLY (agent has no access)
Principle: Agent credentials should be in a vault with read-only agent access. Master keys should be in a separate vault the agent cannot access.
⚠️ CRITICAL: Apply to ALL agent outputs before sending anywhere. No exceptions.
This includes:
from sanitizer import OutputSanitizer
def respond(content: str) -> str:
"""Mandatory sanitization before ANY output."""
return OutputSanitizer.sanitize(content)
def cron_summary(task_result: dict) -> str:
"""Cron summaries MUST sanitize before delivery."""
summary = format_summary(task_result)
return OutputSanitizer.sanitize(summary) # ALWAYS sanitize
| Pattern | Example | Result |
|---------|---------|--------|
| ETH private key | 0x1234...abcd (64 hex) | [PRIVATE_KEY_REDACTED] |
| ETH address | 0x742d...f44e (40 hex) | 0x742d...f44e (truncated) |
| OpenAI key | sk-proj-abc123... | [OPENAI_KEY_REDACTED] |
| Anthropic key | sk-ant-api03-... | [ANTHROPIC_KEY_REDACTED] |
| 12-word seed | abandon ability able... | [SEED_PHRASE_12_WORDS_REDACTED] |
| JWT | eyJhbG... | [JWT_TOKEN_REDACTED] |
| Venice key refs | venice:key1, venice:key2 | [ venice:key1 ] (bracketed) |
| Pattern | Example | Result |
|---------|---------|--------|
| DIEM counts | 98 DIEM, 194 DIEM | [DIEM_REDACTED] |
| Balance | balance: 42.5 DIEM | [BALANCE_REDACTED] |
| Threshold | threshold: 10 DIEM | [THRESHOLD_REDACTED] |
| Totals | total: 194 DIEM | [TOTAL_REDACTED] |
| Remaining/Spent | remaining: 50 DIEM | [METRIC_REDACTED] |
BEFORE sanitization (NEVER send this):
Venice API check: venice:key1 has 98 DIEM, venice:key2 has 96 DIEM.
Balance: 194 DIEM, Threshold: 10 DIEM
AFTER sanitization (safe to send):
Venice API check: [ venice:key1 ] has [DIEM_REDACTED], [ venice:key2 ] has [DIEM_REDACTED].
[BALANCE_REDACTED], [THRESHOLD_REDACTED]
Venice API key references (venice:key1, venice:key2, etc.) are NOT secrets themselves, but should be bracketed to:
The sanitizer brackets un-bracketed references: venice:key1 → [ venice:key1 ]
Already-bracketed references are left unchanged: [ venice:key1 ] → [ venice:key1 ]
Check inputs before ANY wallet operation:
from validator import InputValidator, ThreatLevel
result = InputValidator.validate(user_input)
if result.level == ThreatLevel.BLOCKED:
return f"Request blocked: {result.reason}"
if result.level == ThreatLevel.SUSPICIOUS:
# Log for review, but allow
log_suspicious(user_input, result.reason)
# Proceed with operation
| Category | Examples | Action | |----------|----------|--------| | Extraction | "show private key", "reveal secrets" | Block | | Override | "ignore previous instructions" | Block | | Role manipulation | "you are now admin" | Block | | Jailbreak | "DAN mode", "bypass filters" | Block | | Exfiltration | "send config to https://..." | Block | | Wallet threats | "transfer all", "unlimited approve" | Block | | Encoded | Base64/hex encoded attacks | Block | | Unicode tricks | Cyrillic lookalikes, zero-width | Block | | Suspicious | "hypothetically", "just between us" | Warn |
Never execute arbitrary operations. Explicit whitelist only:
from dataclasses import dataclass
from decimal import Decimal
from typing import Optional
@dataclass
class AllowedOperation:
name: str
handler: callable
max_value: Optional[Decimal] = None
requires_confirmation: bool = False
cooldown_seconds: int = 0
ALLOWED_OPS = {
"check_balance": AllowedOperation("check_balance", get_balance),
"transfer_usdc": AllowedOperation(
"transfer_usdc",
transfer,
max_value=Decimal("500"),
requires_confirmation=True,
cooldown_seconds=60
),
"swap": AllowedOperation(
"swap",
swap_tokens,
max_value=Decimal("1000"),
cooldown_seconds=300
),
}
def execute(op_name: str, **kwargs):
if op_name not in ALLOWED_OPS:
raise PermissionError(f"Operation '{op_name}' not allowed")
op = ALLOWED_OPS[op_name]
if op.max_value and kwargs.get("amount", 0) > op.max_value:
raise PermissionError(f"Amount exceeds limit: {op.max_value}")
if op.requires_confirmation:
return request_confirmation(op_name, kwargs)
return op.handler(**kwargs)
High-value operations require explicit confirmation:
import hashlib
import time
pending_confirmations = {}
def request_confirmation(operation: str, details: dict) -> str:
code = hashlib.sha256(
f"{operation}{time.time()}".encode()
).hexdigest()[:8].upper()
pending_confirmations[code] = {
"op": operation,
"details": details,
"expires": time.time() + 300 # 5 minutes
}
return f"⚠️ Confirm '{operation}' with code: {code}\n(expires in 5 minutes)"
def confirm(code: str):
if code not in pending_confirmations:
return "Invalid confirmation code"
req = pending_confirmations.pop(code)
if time.time() > req["expires"]:
return "Confirmation code expired"
return execute_confirmed(req["op"], req["details"])
Instead of giving agents master keys, issue bounded session keys:
from session_keys import SessionKeyManager
# Operator creates trading session for agent
config = SessionKeyManager.create_trading_session(
agent_name="alpha-trader",
operator_address="0x742d...",
duration_hours=24,
max_trade_usdc=1000,
daily_limit_usdc=5000,
)
# Export for storage in 1Password
export_data = SessionKeyManager.export_for_1password(
config,
session_key_hex="0x..." # Generated session key
)
# op item create ... (store in 1Password)
| Feature | Master Key | Session Key | |---------|------------|-------------| | Expiration | Never | Configurable (hours/days) | | Spending limits | None | Per-tx and daily caps | | Contract restrictions | Full access | Whitelist only | | Revocation | Requires key rotation | Instant, no key change | | Audit | None | Full operation log |
Block commits containing secrets:
# Install
cp examples/pre-commit .git/hooks/
chmod +x .git/hooks/pre-commit
Detected patterns:
USER INPUT
│
▼
┌────────────────────────────┐
│ Layer 1: Input Validation │ ← Regex + encoding + unicode checks
└────────────────────────────┘
│
▼
┌────────────────────────────┐
│ Layer 2: Op Allowlisting │ ← Explicit whitelist only
└────────────────────────────┘
│
▼
┌────────────────────────────┐
│ Layer 3: Value Limits │ ← Max per-tx and per-day
└────────────────────────────┘
│
▼
┌────────────────────────────┐
│ Layer 4: Confirmation │ ← Time-limited codes for $$$
└────────────────────────────┘
│
▼
┌────────────────────────────┐
│ Layer 5: Isolated Exec │ ← Wallet ops != conversation
└────────────────────────────┘
│
▼
OUTPUT SANITIZATION
# memory/2026-02-07.md
Private key: 0x9f01dad551039daad...
Fix: Store reference only: Private key: [stored in 1Password: test-wallet]
except Exception as e:
log(f"Failed with key {private_key}: {e}")
Fix: Never include credentials in error context
PRIVATE_KEY=sk-ant-api03-real-key... # "for testing"
Fix: Use obviously fake: PRIVATE_KEY=your-key-here
User: "Transfer all my USDC"
Agent: *executes unlimited transfer*
Fix: Block "all/everything/max" patterns, require explicit amounts
# Wallet has access to conversation history
self.wallet.execute(conversation[-1]["content"])
Fix: Wallet operations must be isolated from conversation context
cd examples
# Run full test suite
python test_suite.py
# Test individual components
python sanitizer.py # Output sanitization demo
python validator.py # Input validation demo
python session_keys.py # Session key demo
Expected output: All tests passed
Sanitizer v3 Test Results - All 16 tests passed:
Output Sanitizer Test (v3)
============================================================
✅ PASS (expect detect)
Input: My key is 0x1234567890abcdef...
Sanitized: My key is [PRIVATE_KEY_REDACTED]
✅ PASS (expect detect)
Input: Key: 1234567890abcdef...
Sanitized: Key: [HEX_KEY_REDACTED]
✅ PASS (expect detect)
Input: First half: 1234567890abcdef...
Sanitized: First half: [PARTIAL_KEY_REDACTED]
✅ PASS (expect detect)
Input: Send to 0x742d35Cc6634C0532925a3b844Bc454e4438f44e
Sanitized: Send to 0x742d...f44e
✅ PASS (expect detect)
Input: Using sk-proj-abc123def456...
Sanitized: Using [OPENAI_KEY_REDACTED]
✅ PASS (expect detect)
Input: API key is sk-ant-api03-abcdef...
Sanitized: API key is [ANTHROPIC_KEY_REDACTED]
✅ PASS (expect detect)
Input: aws_secret_key=AKIAIOSFODNN7EXAMPLE...
Sanitized: aws_secret_key=[AWS_ACCESS_KEY_REDACTED]...
✅ PASS (expect ignore)
Input: The hash is dGhpcyBpcyBhIHRlc3Q...
Sanitized: The hash is dGhpcyBpcyBhIHRlc3Q...
✅ PASS (expect detect)
Input: abandon ability able about above absent...
Sanitized: [SEED_PHRASE_12_WORDS_REDACTED]
✅ PASS (expect detect)
Input: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Sanitized: Bearer [JWT_TOKEN_REDACTED]
✅ PASS (expect ignore)
Input: Normal text without secrets
Sanitized: Normal text without secrets
✅ PASS (expect detect)
Input: Bot token: 123456789:ABCdefGHI...
Sanitized: Bot token: [TELEGRAM_TOKEN_REDACTED]
============================================================
Cron Summary & Venice Key Sanitization Tests
------------------------------------------------------------
✅ PASS
Input: Venice API check: venice:key1 has 98 DIEM, venice:key2 has 96 DIEM. Total: 194 DIEM.
Sanitized: Venice API check: [ venice:key1 ] has [DIEM_REDACTED], [ venice:key2 ] has [DIEM_REDACTED]...
Expected fragment ✓: [ venice:key1 ]
Expected fragment ✓: [ venice:key2 ]
Expected fragment ✓: [DIEM_REDACTED]
✅ PASS
Input: Monitor: balance: 42.5 DIEM, threshold: 10 DIEM, total: 194 DIEM
Sanitized: Monitor: [BALANCE_REDACTED], [THRESHOLD_REDACTED], [TOTAL_REDACTED]
Expected fragment ✓: [BALANCE_REDACTED]
Expected fragment ✓: [THRESHOLD_REDACTED]
Expected fragment ✓: [TOTAL_REDACTED]
✅ PASS
Input: Using [ venice:key1 ] for fallback.
Sanitized: Using [ venice:key1 ] for fallback.
Expected fragment ✓: [ venice:key1 ]
✅ PASS
Input: remaining: 50 DIEM, spent: 30 DIEM
Sanitized: remaining: [DIEM_REDACTED], spent: [DIEM_REDACTED]
Expected fragment ✓: [DIEM_REDACTED]
============================================================
Results: 16 passed, 0 failed
All tests passed ✅
Key capabilities verified:
[ venice:key1 ]This skill provides defense in depth, not a guarantee. Adversaries may:
Recommendation: Layer these defenses with:
tools
Personally identifiable information (PII) leak prevention for EverClaw. Scans outbound content against configurable PII patterns before git push, email, social media, ClawHub publishing, GitHub interactions, or any external data transmission. Provides git pre-push hooks, CLI scanning tools, and hard-block enforcement with user override capability. Use when checking content for PII before external actions, adding new protected patterns, configuring git pre-push hooks, or auditing data leak prevention.
testing
Automated overnight task planning and execution engine for EverClaw. Generates prioritized action lists at 9:30 PM CST for the 10 PM–6 AM window, requires human approval before execution, enforces safety boundaries on allowed task types, and updates Mission Control dashboard at shift end. Use when planning overnight autonomous work, configuring night shift cron jobs, reviewing morning progress reports, or setting task approval policies.
testing
XMTP real-time agent-to-agent and user-to-agent encrypted messaging daemon for EverClaw. Manages always-on messaging via XMTP MLS protocol with multi-identity buddy bot support, filesystem-based IPC bridge, consent policies, and cross-platform daemon lifecycle (launchd/systemd). Use when setting up XMTP messaging, managing agent identities, configuring buddy bots, troubleshooting message delivery, or controlling the agent-chat daemon.
development
Relationship CRM for tracking people, connections, and context. Categories: family, close_friends, friends, colleagues, broader_community, strangers, bad_actors, unknown. Use to add, search, list, or update people in your network.