Cryptographic Key Management

BIP-39 HD Wallets
for AI Agents

A complete technical guide to provisioning, deriving, and securing hierarchical deterministic wallets for autonomous AI agents. Covers derivation paths for every major chain, secure mnemonic storage patterns, and the recovery procedures that make agent key management reliable in production.


What is BIP-39?

BIP-39 (Bitcoin Improvement Proposal 39) is the standard that defines how a human-readable mnemonic phrase — 12 or 24 words drawn from a 2048-word wordlist — encodes binary entropy that can be used to deterministically derive cryptographic keys.

The process has two stages. First, random binary entropy (128 or 256 bits) is generated. A checksum is appended, and the result is split into 11-bit groups, each mapping to a word in the BIP-39 wordlist. The resulting phrase is the mnemonic.

Second, the mnemonic is converted to a 512-bit seed using PBKDF2-HMAC-SHA512 with the mnemonic as the password, the string "mnemonic" plus an optional passphrase as the salt, and 2048 iterations. That 512-bit seed is the root from which all private keys are derived.

For AI agents, BIP-39 is the ideal key management primitive: a single mnemonic phrase is a complete, portable, chain-agnostic backup of every address the agent will ever use.

bip39_seed.py
from mnemonic import Mnemonic import hashlib, hmac mnemo = Mnemonic("english") # Generate 24-word mnemonic (256-bit entropy) words = mnemo.generate(strength=256) print(words) # "abandon ability able about above..." # Convert mnemonic → 512-bit seed via PBKDF2-HMAC-SHA512 # PBKDF2: 2048 iterations, salt = "mnemonic" + passphrase seed = mnemo.to_seed(words, passphrase="") print(seed.hex()) # 512-bit (64 bytes) root seed # e.g. "5eb00bbddc..." # seed is now ready to feed into BIP-32 derivation

Hierarchical Deterministic
Wallets explained.

BIP-32 defines the Hierarchical Deterministic (HD) wallet standard. Starting from the 512-bit BIP-39 seed, a master private key and master chain code are derived using HMAC-SHA512 with the static key "Bitcoin seed".

From that master key, any number of child keys can be derived by following a derivation path — a sequence of slash-separated index numbers. Each step uses the parent key, the parent chain code, and the child index as input to another HMAC-SHA512 call. An apostrophe (') after an index denotes hardened derivation, which adds 2^31 to the index and produces child keys that cannot be used to reverse-derive the parent.

The practical result: one BIP-39 mnemonic generates an infinite tree of deterministic private keys. The same mnemonic on any BIP-32-compliant software will always produce the same keys at the same paths. For AI agents, this means a single backup phrase covers ETH, BTC, SOL, TRON, and every other chain simultaneously.

hd_derive.py
from mnemonic import Mnemonic from bip32 import BIP32 mnemo = Mnemonic("english") words = mnemo.generate(strength=256) seed = mnemo.to_seed(words) # Create HD wallet from seed bip32 = BIP32.from_seed(seed) # Derive ETH private key at BIP-44 path eth_key = bip32.get_privkey_from_path("m/44'/60'/0'/0/0") # Derive BTC native SegWit key (BIP-84) btc_key = bip32.get_privkey_from_path("m/84'/0'/0'/0/0") # Derive Solana key sol_key = bip32.get_privkey_from_path("m/44'/501'/0'/0'") # Derive TRON key trx_key = bip32.get_privkey_from_path("m/44'/195'/0'/0/0") print(eth_key.hex()) # 32-byte private key

Correct paths
by chain.

Using the wrong derivation path produces valid-looking but incorrect addresses. This table lists the canonical first-account path for every chain that Purple Flea supports. Paths marked with ' after the index use hardened derivation for that component.

Chain Derivation Path Standard Notes
Ethereum (ETH)
m/44'/60'/0'/0/0 BIP-44 Coin type 60. Used by MetaMask, Ledger, and all major ETH wallets.
Bitcoin (BTC)
m/84'/0'/0'/0/0 BIP-84 Native SegWit (bech32). Use m/44'/0'/0'/0/0 for legacy P2PKH addresses.
Solana (SOL)
m/44'/501'/0'/0' SLIP-10 All components hardened. Used by Phantom, Solflare, and Ledger.
TRON (TRX)
m/44'/195'/0'/0/0 BIP-44 Coin type 195. Address is base58check(0x41 + last 20 bytes of EVM address).
BNB Chain (BNB)
m/44'/60'/0'/0/0 BIP-44 Same path as ETH — EVM-compatible chain. Differentiated by chain ID (56), not derivation.
Polygon (MATIC)
m/44'/60'/0'/0/0 BIP-44 Same path as ETH. EVM-compatible — the same private key works on ETH, BNB, and Polygon.

One private key, three EVM chains. Because Ethereum, BNB Chain, and Polygon all use the same elliptic curve (secp256k1) and address format, the private key derived at m/44'/60'/0'/0/0 controls the same public address on all three networks. The chain is selected at the RPC/transaction level, not the derivation level.


Why AI agents should
use BIP-39.

AI agents are stateless by nature — they can be restarted, migrated, containerized, and cloned. BIP-39 HD wallets are designed for exactly this kind of environment.

🔄

Deterministic

The same mnemonic always produces the same addresses at the same derivation paths. An agent can be destroyed and rebuilt from its mnemonic — its wallet identities are preserved exactly.

🔐

Recoverable

If an agent loses its API key, database, or container state entirely, the mnemonic alone is sufficient to reconstruct every address it has ever used on every chain. No key is permanently lost.

🔗

Interoperable standard

BIP-39 is supported by every hardware wallet, every software wallet, and every blockchain library in existence. An agent's addresses are always importable into Ledger, MetaMask, or Phantom for human verification.

🌎

Multi-chain from one backup

A single 24-word phrase covers ETH, BTC, SOL, TRON, BNB Chain, and Polygon simultaneously. Onboarding an agent to a new chain requires no new key material — just a new derivation path.


Secure mnemonic storage
for production agents.

A BIP-39 mnemonic is the master key to every address an agent controls across every chain. Treat it with the same security posture as a root database password — or higher.

Never hardcode a mnemonic in source code. It will end up in git history, container images, and log files. Even in private repositories, hardcoded secrets are one leaked access token away from a total loss of all funds across all chains.

01

Environment variables (development)

For local development and simple deployments, store the mnemonic in an environment variable and read it at runtime. Never commit .env files.

# .env (never commit this file) PURPLEFLEA_MNEMONIC=abandon ability able about above absurd abuse access accident account... # Python — read at runtime import os mnemonic = os.environ["PURPLEFLEA_MNEMONIC"]
02

AWS Secrets Manager (production)

For production deployments, store the mnemonic in a managed secrets service. The agent's IAM role grants read access; no human needs to see the value after initial provisioning.

import boto3, json def get_mnemonic(secret_name: str) -> str: client = boto3.client("secretsmanager", region_name="us-east-1") response = client.get_secret_value(SecretId=secret_name) secret = json.loads(response["SecretString"]) return secret["mnemonic"] # Usage mnemonic = get_mnemonic("prod/agent-alpha/mnemonic")
03

AES-256 encryption at rest

If you must store the mnemonic in a database or file, encrypt it with AES-256-GCM using a key derived from a master password stored separately. Never store the encryption key next to the ciphertext.

from cryptography.hazmat.primitives.ciphers.aead import AESGCM import os, secrets def encrypt_mnemonic(mnemonic: str, key: bytes) -> bytes: # key must be 32 bytes (AES-256) aesgcm = AESGCM(key) nonce = secrets.token_bytes(12) # 96-bit nonce for GCM ciphertext = aesgcm.encrypt(nonce, mnemonic.encode(), None) return nonce + ciphertext # prepend nonce for storage def decrypt_mnemonic(data: bytes, key: bytes) -> str: aesgcm = AESGCM(key) nonce, ciphertext = data[:12], data[12:] return aesgcm.decrypt(nonce, ciphertext, None).decode()
04

HashiCorp Vault (enterprise)

For multi-agent deployments with audit requirements, HashiCorp Vault provides dynamic secrets, fine-grained ACL policies, and a full audit log of every mnemonic access event.

import hvac client = hvac.Client(url="https://vault.internal:8200") client.auth.approle.login(role_id=role_id, secret_id=secret_id) # Read mnemonic from KV v2 secrets engine secret = client.secrets.kv.read_secret_version( path="agents/alpha", mount_point="kv", ) mnemonic = secret["data"]["data"]["mnemonic"]

How Purple Flea
manages agent keys.

When you register an agent with Purple Flea, the platform generates a BIP-39 mnemonic on your behalf, derives all chain addresses from it, and returns the mnemonic to you exactly once — during registration. Purple Flea never stores your plaintext mnemonic after that initial exchange.

Generation

At registration time, Purple Flea generates 256 bits of cryptographically secure entropy using the operating system's CSPRNG (os.urandom on Linux, BCryptGenRandom on Windows). The entropy is passed through the BIP-39 word encoding to produce a 24-word mnemonic. The mnemonic is returned in the registration response — store it immediately and securely.

Server-side encryption

Purple Flea derives the master private key from your mnemonic during registration, then encrypts it with AES-256-GCM using a key stored in a Hardware Security Module (HSM). The ciphertext is stored in the database. The HSM key never leaves the HSM hardware boundary. Even a full database dump reveals nothing without the HSM.

Key rotation

Rotating your agent's API key does not change the underlying mnemonic or derived addresses. Your addresses remain stable. Only the authentication credential changes. To rotate to a new set of addresses, generate a new agent registration — you will receive a new mnemonic.

register_agent.py
import httpx, json, os # Register a new agent via Purple Flea API response = httpx.post( "https://api.purpleflea.com/v1/agent/register", json={"name": "my-trading-agent-v1"}, ) data = response.json() # Save BOTH of these securely — you will NOT see the # mnemonic again after this response. api_key = data["api_key"] # "pf_sk_abc123..." mnemonic = data["mnemonic"] # "word1 word2 ... word24" # Addresses already derived and ready to use eth_addr = data["addresses"]["eth"] btc_addr = data["addresses"]["btc"] sol_addr = data["addresses"]["sol"] # Immediately store mnemonic in Secrets Manager boto3.client("secretsmanager").create_secret( Name="prod/my-trading-agent-v1/mnemonic", SecretString=json.dumps({"mnemonic": mnemonic}), )

Recovering an agent
from the mnemonic.

If your agent loses its API key — through container loss, credential rotation, or an incident — the mnemonic is sufficient to recover full access to the same addresses. The recovery endpoint re-derives all addresses from the mnemonic and issues a new API key.

recover_agent.py
import httpx, boto3, json # Fetch mnemonic from secure storage secret = boto3.client("secretsmanager").get_secret_value( SecretId="prod/my-trading-agent-v1/mnemonic" ) mnemonic = json.loads(secret["SecretString"])["mnemonic"] # POST /agent/recover — supply mnemonic, receive new API key # Purple Flea re-derives all addresses and confirms they match. response = httpx.post( "https://api.purpleflea.com/v1/agent/recover", json={"mnemonic": mnemonic}, ) data = response.json() new_api_key = data["api_key"] # fresh API key # All addresses are IDENTICAL to the originals — # derivation is deterministic, so no funds are moved. assert data["addresses"]["eth"] == original_eth_addr

The recovery endpoint authenticates with the mnemonic alone. Anyone who obtains the mnemonic can issue a new API key and gain full control of the agent's addresses. Protect the mnemonic with the same security posture you would apply to a root password with direct database access.


Keep reading.

Give your agent a
BIP-39 HD wallet.

Purple Flea handles key generation, derivation, and encrypted storage. You keep the mnemonic. Register in under 60 seconds.