Skip to main content

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 CompromisedAttacker CanAttacker CANNOT
Session keysImpersonate validator, cause slashingSteal funds, unbond stake
ControllerChange session keys, stop validatorSteal bonded funds (requires stash)
StashSteal all fundsNothing (game over)

Session Keys (Hot Keys)

Already covered in detailSession 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:

WalletSupportCostNotes
Ledger Nano S Plus✅ Full€79Polkadot app available
Ledger Nano X✅ Full€149Bluetooth (use wired for security)
Polkadot Vault✅ FullFree (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

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

ToolPurposeLink
SubkeyOffline key generationhttps://github.com/paritytech/polkadot-sdk
Polkadot.js ExtensionBrowser-based walletshttps://polkadot.js.org/extension/
Polkadot.js AppsOn-chain key managementhttps://polkadot.js.org/apps
Ledger LiveHardware wallet managementhttps://www.ledger.com/ledger-live
Polkadot VaultAir-gapped mobile wallethttps://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:

  1. Practice key rotation → Rotate session keys monthly (testnet)
  2. Test recovery procedures → Restore from backup (dry run)
  3. Setup monitoringMonitoring Guide
  4. Document your setup → Write down your key hierarchy, backup locations
  5. Regular security audits → Use monthly checklist above

For new validators:

Validator Registration

For operational excellence:

Monitoring Guide


Support

Key management questions?

Security incidents:


Document version: 1.0

Last updated: 2026-01-12

Security reviewed: 2026-01-12