block/buzz · error

--channel requires --relay-key or BUZZ_RELAY_PRIVATE_KEY

Error message

--channel requires --relay-key or BUZZ_RELAY_PRIVATE_KEY

What it means

reconcile_channels() refuses to run in --channel (single-channel force-republish) mode without a signing key. Reconciling one specific channel replaces that channel's existing authoritative kind:39000/39001/39002 snapshot events, so it must be signed by the same stable relay key as the originals — an ephemeral key would silently orphan the snapshot. The key can come from the --relay-key argument or BUZZ_RELAY_PRIVATE_KEY; if neither is present alongside --channel, this error fires. Without --channel, the command proceeds with a warning and an ephemeral key (acceptable for backfilling channels that have no events yet).

Source

Thrown at crates/buzz-admin/src/main.rs:489

    Ok(TenantContext::resolved(record.id, record.host))
}

async fn reconcile_channels(
    channel_arg: Option<String>,
    relay_key_arg: Option<String>,
) -> Result<()> {
    use buzz_core::kind::KIND_NIP29_GROUP_ADMINS;
    use buzz_db::event::EventQuery;

    let db = connect_db().await?;

    // Resolve relay signing key: arg > env > ephemeral. Force-republish must
    // never use an ephemeral key because it replaces an existing authoritative
    // snapshot.
    let configured_relay_key =
        relay_key_arg.or_else(|| std::env::var("BUZZ_RELAY_PRIVATE_KEY").ok());
    if channel_arg.is_some() && configured_relay_key.is_none() {
        return Err(anyhow::anyhow!(
            "--channel requires --relay-key or BUZZ_RELAY_PRIVATE_KEY"
        ));
    }
    let relay_keys = match configured_relay_key {
        Some(key_hex) => {
            Keys::parse(&key_hex).map_err(|e| anyhow::anyhow!("invalid relay key: {e}"))?
        }
        None => {
            let k = Keys::generate();
            eprintln!(
                "Warning: no relay key provided — using ephemeral key {}",
                k.public_key().to_hex()
            );
            eprintln!("Events signed with this key won't be verifiable after this run.");
            eprintln!("Pass --relay-key or set BUZZ_RELAY_PRIVATE_KEY for production use.");
            k
        }
    };

View on GitHub (pinned to f956e6fe06)

Solutions

  1. Pass the relay's signing key explicitly: `buzz-admin reconcile-channels --channel <uuid> --relay-key <64-hex-or-nsec>` (use the SAME key the relay signs with).
  2. Or export BUZZ_RELAY_PRIVATE_KEY=<relay secret key> in the shell and re-run the command unchanged.
  3. If you only meant to backfill channels that have no events yet, drop --channel and run a full reconcile — the ephemeral-key path is allowed there (it prints a warning).

Example fix

# before
buzz-admin reconcile-channels --channel 6f9c...
# error: --channel requires --relay-key or BUZZ_RELAY_PRIVATE_KEY

# after
buzz-admin reconcile-channels --channel 6f9c... --relay-key $BUZZ_RELAY_PRIVATE_KEY
Defensive patterns

Strategy: validation

Validate before calling

# force-republish path requires a key: check flag/env pairing before invoking
if [ "$1" = "--channel" ] || [[ "$*" == *--channel* ]]; then
  [ -n "${BUZZ_RELAY_PRIVATE_KEY:-}" ] || { echo 'pass --relay-key or set BUZZ_RELAY_PRIVATE_KEY' >&2; exit 1; }
fi

Prevention

When it happens

Trigger: `buzz-admin reconcile-channels --channel <uuid>` with neither --relay-key nor BUZZ_RELAY_PRIVATE_KEY in the environment. Typical when an operator escalates from a full reconcile (which tolerated the ephemeral-key warning) to a targeted re-publish of one channel without adding a key.

Common situations: Recovery playbooks that say 're-run reconcile for channel X' but were written assuming the env var was already exported; running from a clean CI shell where only DATABASE_URL/RELAY_URL are set.

Related errors


AI-assisted analysis of block/buzz@f956e6fe06 (2026-08-16). Data as JSON: /api/errors/0e806916c837fdbe. Report an issue: GitHub.