block/buzz · critical · anyhow::Error

RELAY_OWNER_PUBKEY required when BUZZ_REQUIRE_RELAY_MEMBERSH

Error message

RELAY_OWNER_PUBKEY required when BUZZ_REQUIRE_RELAY_MEMBERSHIP=true

What it means

With BUZZ_REQUIRE_RELAY_MEMBERSHIP=true (NIP-43 enforcement) the relay requires a valid RELAY_OWNER_PUBKEY. config.rs strips invalid values with only a warning, so this fatal error fires when the variable is unset or not a valid 64-char hex pubkey — failing fast prevents starting a relay nobody can administer.

Source

Thrown at crates/buzz-relay/src/main.rs:239

        Ok(false) => {}
        Err(e) => {
            error!(
                "Replica fence disabled — floor guard verification failed: {e}. \
                 All cursor reads stay on the writer."
            );
        }
    }

    // NIP-43: if membership enforcement is on, a valid owner pubkey is required.
    // config.rs already strips invalid values with a warning; catch the resulting
    // None here so we fail fast with a clear message rather than starting a relay
    // that no one can administer.
    if config.require_relay_membership && config.relay_owner_pubkey.is_none() {
        error!(
            "BUZZ_REQUIRE_RELAY_MEMBERSHIP=true but RELAY_OWNER_PUBKEY is not set or invalid. \
             Set RELAY_OWNER_PUBKEY to a valid 64-char hex pubkey."
        );
        return Err(anyhow::anyhow!(
            "RELAY_OWNER_PUBKEY required when BUZZ_REQUIRE_RELAY_MEMBERSHIP=true"
        ));
    }

    // NIP-43: relay membership requires a stable signing key.
    // Check this before any DB mutations so we fail fast — no point backfilling
    // or bootstrapping if we'll reject the config anyway.
    if config.require_relay_membership && config.relay_private_key.is_none() {
        return Err(anyhow::anyhow!(
            "BUZZ_RELAY_PRIVATE_KEY is required when BUZZ_REQUIRE_RELAY_MEMBERSHIP=true. \
             NIP-43 events signed with an ephemeral key become unverifiable after restart."
        ));
    }

    // NIP-43 / multi-tenant: seed the deployment's *own* community before any
    // membership backfill or owner bootstrap, so those writes are scoped to a
    // real `(community_id, pubkey)` and not a global pubkey. The host is derived
    // from `relay_url` with the *same* normalization request resolution uses

View on GitHub (pinned to f956e6fe06)

Solutions

  1. Set RELAY_OWNER_PUBKEY to the raw 64-character hex x-only pubkey of the admin key
  2. If the value looks right, strip whitespace/quotes/0x prefix introduced by the secret store
  3. Or set BUZZ_REQUIRE_RELAY_MEMBERSHIP=false if enforcement was enabled by mistake

Example fix

# before
BUZZ_REQUIRE_RELAY_MEMBERSHIP=true
RELAY_OWNER_PUBKEY=npub1abc...xyz   # invalid: bech32, not hex

# after
BUZZ_REQUIRE_RELAY_MEMBERSHIP=true
RELAY_OWNER_PUBKEY=3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d
Defensive patterns

Strategy: validation

Validate before calling

# Assert a valid 64-char hex owner pubkey before boot when enforcement is on.
if [ "$BUZZ_REQUIRE_RELAY_MEMBERSHIP" = "true" ]; then
  [[ "$RELAY_OWNER_PUBKEY" =~ ^[0-9a-fA-F]{64}$ ]] || { echo 'RELAY_OWNER_PUBKEY must be 64 hex chars'; exit 1; }
fi

Type guard

fn is_valid_owner_pubkey(s: &str) -> bool {
    s.len() == 64 && s.bytes().all(|b| b.is_ascii_hexdigit())
}

Prevention

When it happens

Trigger: Enabling membership enforcement without setting RELAY_OWNER_PUBKEY; setting it to an npub1... bech32 string, a 0x-prefixed value, or a truncated/wrong-length key that config.rs discarded.

Common situations: Pasting an NIP-19 npub from a Nostr client instead of raw hex; secret-store truncation or added quotes/whitespace; enabling the flag in a new environment without porting the admin key.

Related errors


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