{"record":{"id":"01ccfd0f0deee447","repo":"block/buzz","slug":"failed-to-sign-moderation-notice-e","errorCode":null,"errorMessage":"failed to sign moderation notice: {e}","messagePattern":"failed to sign moderation notice: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/buzz-relay/src/handlers/moderation_notices.rs","lineNumber":175,"sourceCode":"    //    from `idempotency_ts` (the outbox row's immutable `created_at`). Two\n    //    workers racing on the same outbox row produce byte-identical Nostr events\n    //    (same pubkey + created_at + kind + tags + content = same SHA256 event ID).\n    //    `insert_event`'s ON CONFLICT DO NOTHING ensures exactly one row is\n    //    durably persisted regardless of how many workers reach this point.\n    let source_id = notice.source_id();\n    let tags = vec![\n        Tag::parse([\"h\", &dm_channel_id.to_string()])?,\n        Tag::parse([MODERATION_SOURCE_TAG, &source_id.to_string()])?,\n    ];\n    let ts = nostr::Timestamp::from(idempotency_ts.timestamp() as u64);\n    let event = EventBuilder::new(\n        Kind::Custom(KIND_STREAM_MESSAGE as u16),\n        notice.body(tenant),\n    )\n    .tags(tags)\n    .custom_created_at(ts)\n    .sign_with_keys(&state.relay_keypair)\n    .map_err(|e| anyhow::anyhow!(\"failed to sign moderation notice: {e}\"))?;\n\n    let (stored, _inserted) = state\n        .db\n        .insert_event(tenant.community(), &event, Some(dm_channel_id))\n        .await?;\n\n    let kind_u32 = event_kind_u32(&stored.event);\n    dispatch_persistent_event(tenant, state, &stored, kind_u32, &relay_pubkey_hex, None).await;\n\n    Ok(())\n}\n\n/// Publish the relay-signed kind:0 \"{host} Moderation\" profile so clients can\n/// render the DM author with a recognizable name. Replaceable (NIP-01), so\n/// re-emitting is idempotent — the latest wins.\nasync fn publish_moderation_profile(\n    tenant: &TenantContext,\n    state: &Arc<AppState>,","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/block/buzz/blob/eed74bde2f4797714335ac10c56c0b0244c1def4/crates/buzz-relay/src/handlers/moderation_notices.rs#L157-L193","documentation":"After constructing the moderation notice event, the relay signs it with the relay's own keypair via `sign_with_keys`. This error wraps any failure from that signing operation (map_err into anyhow), meaning the notice event could not be signed and therefore was not stored or delivered.","triggerScenarios":"Calling `send_moderation_notice` when `state.relay_keypair` cannot produce a valid signature — corrupted or zeroed relay secret key, secp256k1 signing failure on the constructed event, or a keypair whose public key does not match the event's pubkey field.","commonSituations":"Relay identity key misconfigured (e.g. empty or invalid secret in env/config); key file corrupted or regenerated between restarts; cryptographic library failing on malformed key material at runtime; keypair loaded from a migration that produced a zero key.","solutions":["Check the relay keypair configuration (.env / key file) and confirm the secret key is a valid 32-byte secp256k1 scalar.","Inspect the wrapped inner error message (`failed to sign moderation notice: {e}`) — it names the underlying secp256k1 cause.","Regenerate or restore the relay identity key from a valid source and restart the relay.","Add a startup check that loads and test-signs with the relay keypair so misconfiguration fails fast."],"exampleFix":"// before — keypair loaded unchecked\nlet keypair = relay_keypair_from_env()?;\n// after — fail fast at startup\nlet keypair = relay_keypair_from_env()?;\nlet probe = EventBuilder::new(Kind::Custom(0), \"health-check\");\nprobe.sign_with_keys(&keypair).context(\"relay keypair is unusable; fix BUZZ_RELAY_KEY\")?;","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match send_moderation_notice(&state, recipient, notice, ts).await {\n  Err(e) if e.to_string().contains(\"failed to sign moderation notice\") => {\n    tracing::error!(cause = %e, \"relay keypair unusable — halting notice delivery\");\n    // page an operator / fail startup rather than retrying blindly\n  }\n  Err(e) => return Err(e),\n  Ok(v) => Ok(v),\n}","preventionTips":["Load and test-sign with the relay keypair at startup so bad key config fails fast.","Keep the relay secret key in a validated secret store, not a hand-edited env file.","Alert on any signing failure — it indicates identity corruption, not a transient fault.","Back up the relay identity key and verify it after restores or migrations."],"tags":["nostr","signing","cryptography","backend","rust","keypair"],"backgroundTag":"signing-key-failure","analyzedSha":"eed74bde2f4797714335ac10c56c0b0244c1def4","analyzedAt":"2026-08-30T13:49:18.474Z","contentChangedAt":"2026-08-30T13:49:18.474Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}