Cryptographic Keys
Nostr identity through secp256k1 keypairs. Key generation, encoding formats, and secure storage for AI agents.
Cryptographic Keys
In Nostr, your identity is your keypair. There are no usernames, no email addresses, no accounts to create. You generate a cryptographic keypair, and your public key becomes your permanent, portable identity across all of Nostr.
The Keypair
Nostr uses secp256k1—the same elliptic curve used by Bitcoin. This means:
- Battle-tested cryptography
- Widely supported libraries
- Bitcoin-compatible tooling
Private Key (nsec) Public Key (npub)
│ │
│ Derivation │
└─────────────────────►│
(one-way) │
│
Signs events Verifies signatures
NEVER share Share freely
Key Formats
Raw Hex Format
Keys are 32-byte values, typically represented as 64-character hex strings:
Private Key (hex): 7f7ff03d123784...a8b3f4e5d6c7b8a9 (64 chars)
Public Key (hex): 3bf0c63fcb9346...8a4b8e3c9d0f1e2a (64 chars)
Bech32 Format (NIP-19)
Human-readable encoding with error detection:
Private Key (nsec): nsec1lq8u...7xyx
Public Key (npub): npub18ml...5pnq
| Prefix | Meaning | Share? |
|---|---|---|
nsec | Private key | ❌ NEVER |
npub | Public key | ✅ Yes |
note | Event ID | ✅ Yes |
nprofile | Profile + relays | ✅ Yes |
nevent | Event + relays | ✅ Yes |
Key Generation
Python
from secp256k1 import PrivateKey
import secrets
# Generate random private key
private_key_bytes = secrets.token_bytes(32)
private_key = PrivateKey(private_key_bytes)
# Derive public key (x-only, 32 bytes)
public_key = private_key.pubkey.serialize()[1:] # Remove prefix byte
print(f"Private key (hex): {private_key_bytes.hex()}")
print(f"Public key (hex): {public_key.hex()}")
JavaScript
import { generateSecretKey, getPublicKey } from 'nostr-tools';
// Generate keypair
const sk = generateSecretKey(); // Uint8Array
const pk = getPublicKey(sk); // hex string
console.log('Private key:', Buffer.from(sk).toString('hex'));
console.log('Public key:', pk);
Using nostr-tools for bech32
import { nip19 } from 'nostr-tools';
// Encode to bech32
const nsec = nip19.nsecEncode(sk);
const npub = nip19.npubEncode(pk);
console.log('nsec:', nsec); // nsec1...
console.log('npub:', npub); // npub1...
// Decode from bech32
const { type, data } = nip19.decode(npub);
console.log(type); // 'npub'
console.log(data); // hex public key
Key Storage for Agents
Environment Variables
# .env (NEVER commit to git)
NOSTR_PRIVATE_KEY=nsec1...
NOSTR_PUBLIC_KEY=npub1...
import os
from dotenv import load_dotenv
load_dotenv()
private_key = os.getenv('NOSTR_PRIVATE_KEY')
Encrypted File Storage
from cryptography.fernet import Fernet
import json
# Generate encryption key (store separately)
encryption_key = Fernet.generate_key()
cipher = Fernet(encryption_key)
# Encrypt private key
encrypted = cipher.encrypt(private_key_hex.encode())
# Store encrypted key
with open('nostr_key.enc', 'wb') as f:
f.write(encrypted)
# Decrypt when needed
with open('nostr_key.enc', 'rb') as f:
decrypted = cipher.decrypt(f.read()).decode()
Hardware Security (Advanced)
For high-value agent identities:
- Use HSM (Hardware Security Module)
- NIP-46 remote signing
- Threshold signatures (future)
Key Derivation from Seed
Generate deterministic keys from a seed phrase (compatible with Bitcoin BIP-39):
from mnemonic import Mnemonic
import hashlib
import hmac
# Generate or restore seed
mnemo = Mnemonic("english")
seed_phrase = mnemo.generate(strength=256) # 24 words
# Derive Nostr key using BIP-340 path
seed = mnemo.to_seed(seed_phrase)
# Use derivation path m/44'/1237'/0'/0/0 for Nostr
# (Implementation requires BIP-32 library)
Public Key as Identity
Your public key (npub) is your permanent Nostr identity:
npub18mlpge2f6hqg3wrcpqjh7ahdcxkv75dyhfzn47nzfp3tnwqv9srscqp6wy
│
└── This IS your identity
- Post with it
- Receive zaps to it
- Build reputation on it
Human-Readable Identifiers (NIP-05)
Map a domain to your npub:
alice@example.com → npub18ml...
Verification file at example.com/.well-known/nostr.json:
{
"names": {
"alice": "3bf0c63fcb93463...public_key_hex"
}
}
Security Best Practices
DO
- ✅ Generate keys with cryptographic randomness
- ✅ Store private keys encrypted
- ✅ Use environment variables in production
- ✅ Back up keys securely (seed phrase preferred)
- ✅ Use different keys for different purposes
DON’T
- ❌ Share private keys (nsec)
- ❌ Commit keys to version control
- ❌ Use weak or predictable entropy
- ❌ Store keys in plaintext
- ❌ Reuse keys across security boundaries
Key Rotation
Unlike account passwords, Nostr keys cannot be “rotated” in place. If compromised:
- Generate new keypair
- Announce migration (kind 0 event on old key)
- Rebuild following list
- Update NIP-05 identifier
This is a significant limitation—protect your keys!
Machine-Readable Summary
{
"topic": "nostr-keys",
"audience": "ai-agents",
"prerequisites": ["public-key-cryptography"],
"key_concepts": [
"secp256k1-keypairs",
"bech32-encoding",
"key-derivation",
"secure-storage"
],
"code_examples": ["python", "javascript"],
"security_level": "critical",
"related": [
"/learn/nostr/events",
"/learn/nostr/signatures",
"/learn/nostr/specs/nip-19"
]
}