block/buzz · critical
BUZZ_RELAY_PRIVATE_KEY must be set when BUZZ_REQUIRE_AUTH_TO
Error message
BUZZ_RELAY_PRIVATE_KEY must be set when BUZZ_REQUIRE_AUTH_TOKEN=true. A stable relay identity is required for production.
What it means
The relay refuses to start in production mode: BUZZ_REQUIRE_AUTH_TOKEN=true requires BUZZ_RELAY_PRIVATE_KEY so the relay has a stable Nostr identity. A persistent pubkey lets replace_addressable_event replace NIP-33 addressable events across restarts; a fresh key each restart would insert duplicates. When auth tokens are not required (dev mode) the relay falls back to a hardcoded, loudly-warned dev keypair.
Source
Thrown at crates/buzz-relay/src/main.rs:511
.unwrap_or(&config.database_url);
let search_pool = sqlx::postgres::PgPoolOptions::new()
.connect(search_db_url)
.await
.map_err(|e| anyhow::anyhow!("Search DB connection failed: {e}"))?;
let search = SearchService::new(search_pool);
info!(
replica = config.read_database_url.is_some(),
"Search service ready (Postgres FTS)"
);
let workflow_config = buzz_workflow::WorkflowConfig::default();
let workflow_engine = Arc::new(WorkflowEngine::new(db.clone(), workflow_config));
config
.media
.validate()
.map_err(|e| anyhow::anyhow!("invalid media config: {e}"))?;
let media_storage = buzz_media::MediaStorage::new(&config.media)
.map_err(|e| anyhow::anyhow!("failed to initialize media storage: {e}"))?;
info!("Media storage connected");
let (app_state, audit_shutdown) = AppState::new(
config.clone(),
db,
redis_health_pool,
audit,
pubsub,
auth,
search,
Arc::clone(&workflow_engine),
relay_keypair,
media_storage,
);
let state = Arc::new(app_state);
// Inter-relay mesh (BUZZ_MESH seam). `boot_mesh` returns None when theView on GitHub (pinned to dad5a33865)
Solutions
- Set BUZZ_RELAY_PRIVATE_KEY to a stable 64-hex-char Nostr secret in the relay environment (generate once and store it as a secret so restarts keep the same relay pubkey)
- Restart the relay and confirm the startup log prints the same pubkey each time
- For throwaway local development only: set BUZZ_REQUIRE_AUTH_TOKEN=false to fall back to the documented hardcoded dev keypair (never do this in production)
Example fix
# .env # before BUZZ_REQUIRE_AUTH_TOKEN=true # (BUZZ_RELAY_PRIVATE_KEY unset) # after BUZZ_REQUIRE_AUTH_TOKEN=true BUZZ_RELAY_PRIVATE_KEY=<64-hex stable secret from your key generator>
Defensive patterns
Strategy: validation
Validate before calling
#!/usr/bin/env bash
# preflight before starting the relay in production mode
if [ "${BUZZ_REQUIRE_AUTH_TOKEN:-false}" = true ]; then
: "${BUZZ_RELAY_PRIVATE_KEY:?BUZZ_RELAY_PRIVATE_KEY must be set when BUZZ_REQUIRE_AUTH_TOKEN=true}"
[[ ${#BUZZ_RELAY_PRIVATE_KEY} -eq 64 && $BUZZ_RELAY_PRIVATE_KEY =~ ^[0-9a-fA-F]+$ ]] \
|| { echo 'BUZZ_RELAY_PRIVATE_KEY must be 64 hex chars'; exit 1; }
fi Prevention
- Store the relay key in a secret manager and inject it into the deployment environment; never rely on the dev fallback in shared environments
- Pin the same key across restarts so NIP-33 addressable events replace instead of duplicating
- Add a deployment healthcheck that fails fast on a missing required env var before the container reaches main()
When it happens
Trigger: Starting buzz-relay with BUZZ_REQUIRE_AUTH_TOKEN=true (or unset-but-defaulting-to-strict behavior) while BUZZ_RELAY_PRIVATE_KEY is missing from the environment; the panic fires during relay key setup in main().
Common situations: Docker/Kubernetes deployment where the secret holding the key is not mounted or the env var name is typo'd; copying .env.example without filling the key; enabling the production auth flag without provisioning the relay identity secret.
Related errors
- invalid BUZZ_RELAY_PRIVATE_KEY: {e}
- Configuration error: {e}
- invalid URL {url:?}: {e}
- an admin cannot ban or time out a community owner or fellow
- git pack cache path must be available
AI-assisted analysis of block/buzz@dad5a33865 (2026-08-20).
Data as JSON: /api/errors/6e38aaf998ee4ad0.
Report an issue: GitHub.