Key Management
Comprehensive guide to managing cryptographic keys for Pilier validators.
This document covers all key types, security practices, backup strategies, and emergency procedures.
Key Types Overview
Pilier validators work with three types of keys:
1. Session Keys (Hot Keys)
├─ AURA (block production)
├─ GRANDPA (finality)
├─ Storage: Validator server
└─ Security: Medium (server compromise = lost)
2. Controller Account (Warm Keys)
├─ Purpose: Manage validator operations
├─ Actions: Set session keys, stop validating
├─ Storage: Software wallet, browser extension
└─ Security: Medium-High
3. Stash Account (Cold Keys)
├─ Purpose: Hold bonded funds, receive rewards
├─ Actions: Bond/unbond, change controller
├─ Storage: Hardware wallet, paper wallet
└─ Security: High (offline storage)
Security model:
Stash (Cold)
└─ Controls → Controller (Warm)
└─ Controls → Session Keys (Hot)
Compromise impact:
| Key Compromised | Attacker Can | Attacker CANNOT |
|---|---|---|
| Session keys | Impersonate validator, cause slashing | Steal funds, unbond stake |
| Controller | Change session keys, stop validator | Steal bonded funds (requires stash) |
| Stash | Steal all funds | Nothing (game over) |
Session Keys (Hot Keys)
Already covered in detail → Session Keys Guide
Quick reference:
Purpose: Consensus operations (block signing, finality voting)
Storage: /var/lib/pilier/chains/*/keystore/
Backup: Encrypted keystore backup + secret phrases
Rotation: Every 6-12 months (or immediately if compromised)
Key security measures:
# Correct permissions
sudo chown pilier:pilier /var/lib/pilier/chains/*/keystore/*
sudo chmod 600 /var/lib/pilier/chains/*/keystore/*
# Firewall (no RPC exposed publicly)
sudo ufw deny 9933/tcp
sudo ufw deny 9944/tcp
# Regular backups
# (see backup script in session-keys.md)
Controller Account (Warm Keys)
Purpose
The controller account manages validator operations without holding large funds.
What controller can do:
✅ Set session keys (session.setKeys)
✅ Stop validating (session.purgeKeys)
✅ Set validator commission (validatorPrefs.setCommission)
✅ Nominate other validators (if not validating yourself)
What controller CANNOT do:
❌ Unbond funds (requires stash)
❌ Transfer bonded funds (requires stash)
❌ Change controller account (requires stash)
Storage Options
Option A: Browser Extension (Convenience)
Tools: Polkadot.js Extension, Talisman, SubWallet
Pros: ✅ Easy to use (click to sign)
✅ Good for frequent operations
✅ Free
Cons: ⚠️ Vulnerable to browser exploits
⚠️ Vulnerable to malware
⚠️ Requires browser (desktop/mobile)
Best for: Testnet validators, low-stake mainnet
Installation (Polkadot.js Extension):
1. Chrome: https://chrome.google.com/webstore → Search "Polkadot.js extension"
2. Firefox: https://addons.mozilla.org → Search "Polkadot.js extension"
3. Install extension
4. Create account or import seed phrase
5. Set strong password (required to sign transactions)
Option B: Mobile Wallet (Balance of Security & Convenience)
Tools: Nova Wallet, Fearless Wallet
Pros: ✅ Biometric unlock (Face ID, fingerprint)
✅ Easier than hardware wallet
✅ Can be offline (airplane mode) except when signing
Cons: ⚠️ Vulnerable if phone stolen/hacked
⚠️ App vulnerabilities
Best for: Active validators (frequent key changes)
Option C: CLI with Keyfile (Advanced)
Tools: polkadot-js-api CLI, subkey
Pros: ✅ No GUI needed (server-friendly)
✅ Scriptable (automation)
Cons: ⚠️ Keyfile on disk (must be encrypted)
⚠️ Requires technical knowledge
Best for: Automation, air-gapped signing
Example usage:
# Store controller key in encrypted JSON
polkadot-js-api \
--ws wss://testnet-rpc.pilier.net \
--seed "$(cat controller-seed.txt)" \
tx.session.setKeys \
"0x1234..." \
"0x00"
# controller-seed.txt contains secret phrase (chmod 600!)
Security Best Practices
DO:
- ✅ Use separate controller and stash (never use same account)
- ✅ Strong password for browser extension (20+ characters, unique)
- ✅ Write down seed phrase (12/24 words on paper, not digital)
- ✅ Backup seed phrase (multiple copies, different locations)
- ✅ Enable 2FA on devices with controller access
- ✅ Keep controller balance low (~1-10 PIL for transaction fees)
DON'T:
- ❌ Never store seed phrase digitally (text file, email, cloud)
- ❌ Never screenshot seed phrase (malware can steal images)
- ❌ Never use same controller for multiple validators (blast radius)
- ❌ Never share controller access (even with team members)
Controller Setup
Step 1: Generate controller account
Via Polkadot.js Extension:
1. Open extension
2. Click "+" → "Create new account"
3. **Save seed phrase** (write on paper immediately!)
4. Set account name: "Pilier Validator Controller"
5. Set strong password
6. Click "Add the account with the generated seed"
Via Subkey (offline):
# Generate ed25519 account (Substrate default)
subkey generate
# Output:
Secret phrase: word1 word2 ... word12
Network ID: substrate
Secret seed: 0xabcd...
Public key: 0x1234...
Account ID: 0x1234...
SS58 Address: 5GNJqTPy...
# Write down secret phrase!
# Import to Polkadot.js Extension: "Import account from pre-existing seed"
Step 2: Fund controller account
# Controller needs small balance for transaction fees
# Recommended: 1-10 PIL
# Transfer from faucet (testnet) or existing account:
# Use Polkadot.js Apps → Accounts → Send
# Verify balance:
polkadot-js-api --ws wss://testnet-rpc.pilier.net \
query.system.account 5GNJqTPy...
Step 3: Link controller to stash
This is done from the stash account:
Via Polkadot.js Apps:
1. Network → Staking → Account Actions
2. Click "+ Stash" (if first time validating)
3. Fill form:
- Stash account: [Your stash account]
- Controller account: [Your controller account]
- Value bonded: [Amount to bond, e.g., 1000 PIL]
- Payment destination: [Stash account (increase the amount at stake)]
4. Click "Bond" → Sign with stash account
5. Wait for transaction confirmation
CLI alternative:
polkadot-js-api \
--ws wss://testnet-rpc.pilier.net \
--seed "stash-secret-phrase" \
tx.staking.bond \
"5GNJq...controller-address" \
1000000000000000 \
"Staked"
Controller Rotation
When to change controller:
- 🔐 Controller key compromised or suspected leak
- 🔄 Regular rotation policy (every 12 months)
- 👥 Change of validator operator (personnel change)
How to rotate:
Step 1: Generate new controller account (same as setup above)
Step 2: Set new controller (from stash)
Via Polkadot.js Apps:
1. Network → Staking → Account Actions
2. Find your validator → Click "..." → "Change controller account"
3. Select new controller account
4. Sign with **stash account** (not old controller!)
5. Wait for confirmation
CLI:
polkadot-js-api \
--ws wss://testnet-rpc.pilier.net \
--seed "stash-secret-phrase" \
tx.staking.setController \
"5Hy9K...new-controller-address"
Step 3: Verify new controller active
# Query controller for your stash
polkadot-js-api --ws wss://testnet-rpc.pilier.net \
query.staking.bonded YOUR_STASH_ADDRESS
# Output: 5Hy9K...new-controller-address
Old controller is now useless (can delete safely after verification).
Stash Account (Cold Keys)
Purpose
The stash account holds your bonded funds and receives validator rewards.
What stash can do:
✅ Bond funds (lock PIL for validation)
✅ Unbond funds (start 28-day unlocking period)
✅ Withdraw unbonded funds (after 28 days)
✅ Change controller account
✅ Set payment destination (where rewards go)
✅ Transfer funds (unbonded balance only)
Critical: If stash is compromised, all funds can be stolen. Maximum security required.
Storage Options
Option A: Hardware Wallet (RECOMMENDED for Mainnet)
Tools: Ledger Nano S/X, Polkadot Vault (formerly Parity Signer)
Pros: ✅ Private keys never leave device
✅ Immune to malware (air-gapped or isolated chip)
✅ Physical confirmation required (button press)
Cons: ⚠️ Cost (€50-150 for Ledger)
⚠️ Less convenient (requires device for every tx)
⚠️ Can be lost/damaged (need backup seed phrase)
Best for: Mainnet validators with significant stake
Supported hardware wallets:
| Wallet | Support | Cost | Notes |
|---|---|---|---|
| Ledger Nano S Plus | ✅ Full | €79 | Polkadot app available |
| Ledger Nano X | ✅ Full | €149 | Bluetooth (use wired for security) |
| Polkadot Vault | ✅ Full | Free (old phone) | Open-source, air-gapped app |
Option B: Paper Wallet (Maximum Security, Low Convenience)
Tools: Subkey (offline computer)
Pros: ✅ Completely offline (no digital attack surface)
✅ Free
✅ Immune to hacking (paper doesn't get viruses)
Cons: ⚠️ Very inconvenient (import seed every time you sign)
⚠️ Physical security required (fire, water, theft)
⚠️ Easy to lose
Best for: Long-term holders, rarely signing transactions
How to create paper wallet:
# On air-gapped computer (no internet):
# 1. Boot Ubuntu from USB (live mode, no persistence)
# 2. Install subkey (from USB, not internet)
# 3. Generate account
subkey generate --scheme sr25519
# Output:
Secret phrase: word1 word2 word3 ... word12
SS58 Address: 5GNJqTPy...
# 4. Write seed phrase on paper (NEVER digital!)
# 5. Optionally: Use steel plate for fire/water resistance
# 6. Store in safe, safety deposit box, etc.
# 7. Destroy all digital copies (secure delete)
Option C: Multi-Signature Wallet (Enterprise)
Tools: Polkadot.js Apps Multisig
Pros: ✅ Requires M-of-N signatures (e.g., 2-of-3)
✅ No single point of failure
✅ Good for teams, foundations
Cons: ⚠️ Complex setup
⚠️ Coordination overhead (need multiple signers)
⚠️ Higher transaction fees (multiple signatures)
Best for: Institutional validators, high-value stashes
Example setup (2-of-3 multisig):
Signers:
├─ Alice (hardware wallet)
├─ Bob (hardware wallet)
└─ Charlie (paper wallet backup)
Any transaction requires 2 of 3 signatures.
Use case:
- Alice + Bob: Normal operations
- Alice + Charlie: If Bob unavailable
- Bob + Charlie: If Alice unavailable
Stash Setup
Step 1: Generate stash account
For testnet (browser extension OK):
Polkadot.js Extension → Create new account
For mainnet (hardware wallet REQUIRED):
1. Purchase Ledger device
2. Initialize Ledger (follow device instructions)
3. Install Polkadot app:
- Ledger Live → Manager → Search "Polkadot" → Install
4. Connect to Polkadot.js Apps:
- Polkadot.js Apps → Settings → Manage Hardware Connections
- "Attach Ledger via USB" → Allow
5. Accounts → Add via Ledger → Select account
Step 2: Secure seed phrase
Physical backup (CRITICAL):
Write down seed phrase (12 or 24 words):
├─ Use pen and paper (NO pencil - fades)
├─ Write clearly, verify spelling
├─ Create 2-3 copies
└─ Store in different locations:
├─ Home safe
├─ Bank safety deposit box
└─ Trusted family member (different city)
Optional: Steel plate backup
├─ Product: Cryptosteel, Billfodl
├─ Fire-resistant (up to 1400°C)
├─ Water-resistant
└─ Lasts centuries
Test recovery BEFORE funding:
1. Write down seed phrase
2. Delete account from wallet
3. Restore account from seed phrase
4. Verify address matches original
5. If successful → safe to fund
Step 3: Fund stash account
# Testnet: Get test PIL from faucet
https://testnet.pilier.net/faucet
# Mainnet: Purchase PIL or transfer from exchange
# Recommended minimum stake: 1000 PIL (varies by network requirements)
Step 4: Bond funds
Via Polkadot.js Apps:
1. Network → Staking → Account Actions
2. Click "+ Stash"
3. Fill form:
- Stash account: [Your hardware wallet account]
- Controller account: [Your controller account, see above]
- Value bonded: [Amount, e.g., 1000 PIL]
- Payment destination: [Staked (compound rewards)]
4. Click "Bond"
5. Sign with stash (Ledger: confirm on device)
6. Wait for confirmation
Verify bonded:
polkadot-js-api --ws wss://testnet-rpc.pilier.net \
query.staking.ledger YOUR_CONTROLLER_ADDRESS
# Output:
{
stash: "5GNJq...your-stash",
total: 1000000000000000, # 1000 PIL (15 zeros = 10^15 = 1 PIL in smallest unit)
active: 1000000000000000,
unlocking: []
}
Stash Security Best Practices
DO:
- ✅ Use hardware wallet for mainnet (non-negotiable)
- ✅ Test seed phrase recovery before funding
- ✅ Multiple physical backups (2-3 copies, different locations)
- ✅ Verify addresses before sending large amounts
- ✅ Use multisig for institutional/high-value stashes
- ✅ Keep stash offline (only connect when absolutely necessary)
- ✅ Set payment destination to "Staked" (auto-compound rewards)
DON'T:
- ❌ Never store seed phrase digitally (text file, cloud, screenshot)
- ❌ Never share seed phrase (not even with support, devs, "validators")
- ❌ Never type seed phrase on internet-connected computer
- ❌ Never use brain wallet ("my-secret-password" = easily cracked)
- ❌ Never use same stash for multiple validators (increases risk)
- ❌ Never connect stash to untrusted dApps (phishing risk)
Stash Recovery
If stash seed phrase lost:
Impact: All bonded funds are permanently lost. No recovery possible.
Prevention:
Redundancy levels:
├─ Level 1 (Minimum): 2 copies, different locations
├─ Level 2 (Good): 3 copies, different cities
├─ Level 3 (Excellent): 2-of-3 multisig (no single seed phrase)
└─ Level 4 (Paranoid): Shamir's Secret Sharing (split seed into shards)
If stash key compromised:
Immediate actions:
# 1. Transfer all unbonded funds to new address (FAST!)
# Via Polkadot.js Apps or CLI
# 2. Start unbonding process (bonded funds)
# Network → Staking → Account Actions → Unbond
# WARNING: 28-day unbonding period (funds at risk during this time!)
# 3. After 28 days: Withdraw unbonded funds to new address
# 4. Report to Pilier team (validators@pilier.net)
Prevention is better than cure:
- Use hardware wallet (attacker needs physical device)
- Use multisig (attacker needs multiple keys)
- Monitor stash balance daily (detect unauthorized txs early)
Key Hierarchy & Relationships
Typical Setup
Stash (Cold)
├─ Holds: 10,000 PIL (bonded)
├─ Receives: Validator rewards
├─ Storage: Ledger hardware wallet
└─ Controls:
│
Controller (Warm)
├─ Holds: 5 PIL (tx fees only)
├─ Actions: Set session keys, change commission
├─ Storage: Browser extension
└─ Controls:
│
Session Keys (Hot)
├─ AURA (sr25519): Block production
├─ GRANDPA (ed25519): Finality voting
├─ Storage: Validator server keystore
└─ Actions: Automatic (no manual signing)
Setting Up Complete Validator
Step-by-step process:
1. Generate stash account (hardware wallet)
└─ Secure seed phrase (multiple backups)
2. Fund stash account (e.g., 10,000 PIL)
└─ Verify balance before proceeding
3. Generate controller account (browser extension)
└─ Fund with small amount (5 PIL)
4. Bond funds from stash
└─ Link stash → controller
└─ Set payment destination
5. Start validator node
└─ Install binary, configure systemd
6. Generate session keys (on validator server)
└─ curl author_rotateKeys
7. Set session keys (from controller account)
└─ Submit session.setKeys transaction
8. Wait for session change (~1 hour)
└─ Monitor logs for "authority key"
9. Verify validator active
└─ Check telemetry, block production
10. Setup monitoring & alerts
└─ Prometheus, Grafana, health checks
Backup Strategies
3-2-1 Backup Rule
For validator keys (session, controller, stash):
3 copies:
├─ Primary: Hardware wallet / keystore on server
├─ Backup 1: Encrypted file on USB drive
└─ Backup 2: Paper wallet in safe
2 different media:
├─ Digital: Encrypted files
└─ Physical: Paper/steel plate
1 off-site:
└─ Bank safety deposit box, trusted family
Backup Checklist
What to backup:
- Session keys keystore (/var/lib/pilier/chains/*/keystore/)
- Session keys seed phrases (if generated manually)
- Controller account seed phrase
- Stash account seed phrase
- Validator configuration (systemd service, config.toml)
- Account addresses (stash, controller, session public keys)
Where to store:
- Encrypted USB drive (session keys keystore, config files)
- Password manager (Bitwarden, 1Password - encrypted seeds)
- Paper wallet (stash seed phrase - fire-resistant safe)
- Steel plate (stash seed phrase - bank safety deposit box)
- Cloud backup (ONLY if strongly encrypted: GPG, VeraCrypt)
Backup Script (Session Keys)
#!/bin/bash
# backup-validator-keys.sh
set -e
BACKUP_DIR="/secure-backup/pilier-$(date +%Y%m%d-%H%M%S)"
KEYSTORE="/var/lib/pilier/chains/pilier_testnet/keystore"
CONFIG="/etc/pilier"
echo "Creating backup: $BACKUP_DIR"
# Create backup directory
mkdir -p "$BACKUP_DIR"
# Backup keystore
echo "Backing up keystore..."
cp -r "$KEYSTORE" "$BACKUP_DIR/"
# Backup config files
echo "Backing up config..."
cp -r "$CONFIG" "$BACKUP_DIR/"
# Backup systemd service
cp /etc/systemd/system/pilier.service "$BACKUP_DIR/"
# Create list of accounts (for reference)
echo "# Pilier Validator Accounts" > "$BACKUP_DIR/accounts.txt"
echo "# Generated: $(date)" >> "$BACKUP_DIR/accounts.txt"
echo "" >> "$BACKUP_DIR/accounts.txt"
echo "Stash: [MANUALLY FILL]" >> "$BACKUP_DIR/accounts.txt"
echo "Controller: [MANUALLY FILL]" >> "$BACKUP_DIR/accounts.txt"
echo "Session Keys: [MANUALLY FILL]" >> "$BACKUP_DIR/accounts.txt"
# Create encrypted archive
echo "Creating encrypted archive..."
tar -czf "$BACKUP_DIR.tar.gz" "$BACKUP_DIR/"
gpg --symmetric --cipher-algo AES256 "$BACKUP_DIR.tar.gz"
# Verify encrypted file created
if [ -f "$BACKUP_DIR.tar.gz.gpg" ]; then
echo "✓ Backup created: $BACKUP_DIR.tar.gz.gpg"
echo "✓ Size: $(du -h $BACKUP_DIR.tar.gz.gpg | cut -f1)"
# Clean up plaintext
rm -rf "$BACKUP_DIR" "$BACKUP_DIR.tar.gz"
echo ""
echo "IMPORTANT:"
echo "1. Copy $BACKUP_DIR.tar.gz.gpg to USB drive"
echo "2. Store USB drive in safe place"
echo "3. Remember encryption password!"
else
echo "✗ Backup failed!"
exit 1
fi
Run backup monthly:
# Add to crontab (run on 1st of each month)
crontab -e
# Add:
0 3 1 * * /usr/local/bin/backup-validator-keys.sh
Restore from Backup
Restore session keys:
# 1. Decrypt backup
gpg --decrypt pilier-20260201.tar.gz.gpg > pilier-20260201.tar.gz
# 2. Extract
tar -xzf pilier-20260201.tar.gz
# 3. Verify contents
ls pilier-20260201/keystore/
# Should see: 61757261... (AURA) and 6772616e... (GRANDPA)
# 4. Stop node
sudo systemctl stop pilier
# 5. Restore keystore
sudo cp -r pilier-20260201/keystore/* /var/lib/pilier/chains/pilier_testnet/keystore/
sudo chown pilier:pilier /var/lib/pilier/chains/pilier_testnet/keystore/*
sudo chmod 600 /var/lib/pilier/chains/pilier_testnet/keystore/*
# 6. Restore config (if needed)
sudo cp pilier-20260201/pilier.service /etc/systemd/system/
sudo systemctl daemon-reload
# 7. Start node
sudo systemctl start pilier
# 8. Verify keys loaded
sudo journalctl -u pilier -n 20 | grep -i key
Restore controller/stash (from seed phrase):
1. Open Polkadot.js Extension
2. Click "+" → "Import account from pre-existing seed"
3. Enter seed phrase (12 or 24 words)
4. Set account name and password
5. Verify address matches expected
Emergency Procedures
Scenario 1: Session Keys Compromised
Indicators:
- Unauthorized blocks signed by your validator
- Slashing event (equivocation)
- Multiple instances of validator running (telemetry shows duplicates)
Response (execute in order):
# 1. IMMEDIATELY stop validator
sudo systemctl stop pilier
# 2. Rotate session keys (generate new)
# On DIFFERENT server or offline:
subkey generate --scheme sr25519 # New AURA
subkey generate --scheme ed25519 # New GRANDPA
# 3. Set new keys on-chain (from controller)
# Use Polkadot.js Apps → session.setKeys with new public keys
# 4. Clean compromised server
# Option A: Fresh OS reinstall (best)
# Option B: Secure existing server:
sudo rm -rf /var/lib/pilier/chains/*/keystore/*
sudo apt update && sudo apt upgrade -y
# Change SSH keys, passwords, etc.
# 5. Insert new session keys on clean server
pilier-node key insert ... (with new seed phrases)
# 6. Restart validator
sudo systemctl start pilier
# 7. Report incident
# Email: validators@pilier.net
# Include: Timestamp, validator account, suspected cause
Downtime: ~1 hour (until new session keys active)
Funds at risk: No (session keys cannot access bonded funds)
Scenario 2: Controller Compromised
Indicators:
- Unauthorized session key changes
- Validator stopped without your action
- Unknown transactions from controller account
Response:
# 1. Change controller (from stash - URGENT!)
# Via Polkadot.js Apps → Staking → Change Controller
# 2. Generate new controller account
# Use new seed phrase (NOT related to old controller)
# 3. Fund new controller (small amount for tx fees)
# 4. Set session keys again (from new controller)
# In case attacker changed them
# 5. Monitor old controller
# Watch for further unauthorized activity
# 6. Investigate breach
# How did attacker get controller access?
# - Malware on computer?
# - Phishing attack?
# - Leaked seed phrase?
Downtime: None (if you act fast, validator continues running)
Funds at risk: Minimal (only controller balance, ~5 PIL)
Scenario 3: Stash Compromised
Indicators:
- Unauthorized unbonding started
- Funds transferred from stash
- Unknown transaction signed by stash
Response (EXTREMELY URGENT):
# 1. Transfer unbonded funds IMMEDIATELY
# Via Polkadot.js Apps or CLI → Transfer to new secure address
# DO THIS FIRST - every second counts!
# 2. Bonded funds: Start competing unbonding
# You cannot stop attacker's unbonding
# But you can unbond to different address (if you act first)
# Via Polkadot.js Apps → Staking → Unbond
# Set withdraw address to NEW secure stash
# 3. If unbonding period started:
# Wait 28 days
# Withdraw to new address IMMEDIATELY when unlocked
# Monitor daily (set calendar alerts!)
# 4. Report to Pilier team
# Email: validators@pilier.net
# May be able to help if caught early
# 5. Post-mortem
# How was stash compromised?
# - Hardware wallet stolen?
# - Seed phrase photographed?
# - Phishing attack?
# - Malware on signing device?
Downtime: Validator stops (no funds to bond)
Funds at risk: ALL bonded funds (worst-case scenario)
Prevention: Use hardware wallet + multisig for mainnet!
Scenario 4: Server Compromised
Indicators:
- Unusual CPU/network activity
- Unknown processes running
- SSH login from unknown IP
- Modified system files
Response:
# 1. Stop validator immediately
sudo systemctl stop pilier
# 2. Isolate server (disconnect network if possible)
sudo ufw deny out
sudo ufw deny in
# Allow only your IP for SSH:
sudo ufw allow from YOUR_IP to any port 22
# 3. Backup session keys (if still intact)
sudo cp -r /var/lib/pilier/chains/*/keystore /tmp/keystore-backup
# 4. Rotate session keys (assume compromised)
# Generate new keys on DIFFERENT machine
# 5. Forensics (optional, for investigation)
# - Check auth logs: sudo cat /var/log/auth.log
# - Check running processes: ps aux
# - Check network connections: sudo netstat -tulpn
# 6. Clean installation recommended
# Option A: Nuke and repave (best)
# - Fresh OS install
# - Restore from clean backup
# - Insert new session keys
# Option B: Harden existing (if certain attack vector closed)
# - Change all passwords
# - Regenerate SSH keys
# - Update all software
# - Install IDS (fail2ban, ossec)
# 7. Set new session keys on-chain
# 8. Restart validator on clean server
# 9. Report incident
# Help improve validator security for all
Key Rotation Schedule
Recommended Schedule
Testnet:
├─ Session keys: Every 3 months (practice for mainnet)
├─ Controller: Every 12 months
└─ Stash: Never (unless compromised)
Mainnet:
├─ Session keys: Every 6-12 months
├─ Controller: Every 12 months
└─ Stash: Never (too risky, use multisig instead)
Rotation Calendar Template
2026:
├─ February: Launch validator (generate all keys)
├─ May: Rotate session keys (Q2)
├─ August: Rotate session keys (Q3)
├─ November: Rotate session keys (Q4)
2027:
├─ February: Rotate controller + session keys (annual)
├─ May: Rotate session keys (Q2)
├─ August: Rotate session keys (Q3)
├─ November: Rotate session keys (Q4)
Set calendar reminders:
Recurring event:
- Title: "Rotate Pilier Session Keys"
- Frequency: Every 3 months
- Reminder: 1 week before (prep time)
- Notes: Link to session-keys.md rotation guide
Security Audit Checklist
Monthly Security Review
Use this checklist every month:
Session Keys:
- Keystore files have correct permissions (600, owner: pilier)
- Keystore backup is recent (<30 days old)
- Server firewall is active (RPC ports closed publicly)
- No unauthorized access in SSH logs
- Validator producing blocks normally (telemetry green)
Controller:
- Controller balance sufficient for tx fees (≥1 PIL)
- No unauthorized transactions from controller
- Browser extension password is strong
- Seed phrase backup is secure (not digital)
- 2FA enabled on device with controller access
Stash:
- Stash balance unchanged (except rewards)
- Bonded amount correct (no unauthorized unbonding)
- Hardware wallet firmware up-to-date
- Seed phrase backups in 2+ locations
- Payment destination = "Staked" (compounding rewards)
Server:
- OS security updates applied (unattended-upgrades)
- Validator node version up-to-date
- Disk space >20% free
- Memory usage <80%
- CPU temperature normal (<80°C)
- fail2ban active (SSH brute-force protection)
Tools & Resources
Key Generation Tools
| Tool | Purpose | Link |
|---|---|---|
| Subkey | Offline key generation | https://github.com/paritytech/polkadot-sdk |
| Polkadot.js Extension | Browser-based wallets | https://polkadot.js.org/extension/ |
| Polkadot.js Apps | On-chain key management | https://polkadot.js.org/apps |
| Ledger Live | Hardware wallet management | https://www.ledger.com/ledger-live |
| Polkadot Vault | Air-gapped mobile wallet | https://github.com/paritytech/parity-signer |
Security Tools
# Fail2ban (SSH brute-force protection)
sudo apt install fail2ban
sudo systemctl enable fail2ban
# AIDE (file integrity monitoring)
sudo apt install aide
sudo aideinit
# Lynis (security auditing)
sudo apt install lynis
sudo lynis audit system
# ClamAV (antivirus)
sudo apt install clamav
sudo freshclam # Update virus definitions
sudo clamscan -r /home/pilier/
Backup Tools
# GPG (encryption)
gpg --version # Should be installed by default
# Duplicity (encrypted incremental backups)
sudo apt install duplicity
# Restic (modern backup tool)
sudo apt install restic
# rclone (cloud storage sync, with encryption)
sudo apt install rclone
FAQ
Can I use the same keys for testnet and mainnet?
No! Never reuse keys across networks.
Why:
- Testnet security is lower (acceptable risk)
- If testnet keys leaked → mainnet validator unaffected
- Key confusion can lead to mistakes (wrong network)
Always generate separate keys:
- Testnet: Stash, controller, session keys
- Mainnet: Different stash, controller, session keys
What if I forget my hardware wallet PIN?
Ledger:
- After 3 wrong attempts → device resets (wipes keys)
- Recovery: Import seed phrase to new device
- Prevention: Test recovery BEFORE funding
Polkadot Vault:
- No PIN recovery (device must be reset)
- Recovery: Create new wallet, import seed phrase
How do I transfer validator to someone else?
Option A: Transfer stash (full transfer)
Steps:
1. Unbond all funds (28-day wait)
2. Withdraw funds to new owner's address
3. New owner bonds funds with their controller
4. New owner generates session keys
5. Validator transferred ✓
Downtime: 28 days (unbonding period)
Option B: Change controller only (operational transfer)
Steps:
1. Generate new controller for new operator
2. Change controller from stash (staking.setController)
3. New operator generates session keys
4. New operator sets session keys
5. Operational control transferred (stash remains with you) ✓
Downtime: ~1 hour (session change)
Most common: Option B (change operators, keep ownership)
Should I use BIP39 passphrases?
BIP39 passphrase = 25th word (optional extra word)
Pros:
- ✅ Additional security layer (attacker needs seed + passphrase)
- ✅ Plausible deniability (main wallet = small amount, passphrase wallet = real funds)
- ✅ Protects against physical seed theft
Cons:
- ⚠️ Easy to forget (if not written down → funds lost forever)
- ⚠️ Another secret to backup securely
- ⚠️ Not all wallets support BIP39 passphrases
Recommendation:
- Yes for stash account (mainnet, high-value)
- No for controller/session keys (unnecessary complexity)
If using passphrase:
- Treat it like second seed phrase (paper backup, safe storage)
- Test recovery WITH passphrase before funding
What's the best password manager for crypto?
Recommended:
- Bitwarden (open-source, self-hostable)
- 1Password (excellent security, user-friendly)
- KeePassXC (offline, local storage)
Store in password manager:
- ✅ Controller seed phrase (encrypted)
- ✅ Session key seed phrases (if generated manually)
- ✅ Hardware wallet PIN (if you're confident in PM security)
- ✅ Backup encryption passwords
Do NOT store in password manager:
- ❌ Stash seed phrase (too valuable, use paper/steel)
Next Steps
After mastering key management:
- Practice key rotation → Rotate session keys monthly (testnet)
- Test recovery procedures → Restore from backup (dry run)
- Setup monitoring → Monitoring Guide
- Document your setup → Write down your key hierarchy, backup locations
- Regular security audits → Use monthly checklist above
For new validators:
For operational excellence:
Support
Key management questions?
- Forum: forum.pilier.net/security
- Telegram: t.me/pilier_validators
- Email: validators@pilier.net
Security incidents:
- Emergency email: security@pilier.org
- Response time: <24 hours
Document version: 1.0
Last updated: 2026-01-12
Security reviewed: 2026-01-12