block/buzz · error · anyhow::Error
an admin cannot ban or time out a community owner or fellow…
Error message
an admin cannot ban or time out a community owner or fellow admin
What it means
Relay-side moderation authorization: an admin holds every moderation capability except ban and timeout against the community owner or a fellow admin — only the owner may action an admin. The target's role is read from relay_members; a target with no membership row (e.g. a drive-by spammer who left) is still bannable. Unban/Untimeout lift restrictions and are intentionally not blocked at this seam.
Solutions
- Have the community owner issue the ban/timeout instead
- Or have the owner demote the target admin first, then any admin can act
- For lifting a restriction, use unban/untimeout — that path is intentionally allowed and audited
Example fix
# before (admin tries to ban a fellow admin) buzz moderation ban --user <admin-pubkey> # 403: an admin cannot ban or time out a community owner or fellow admin # after (owner acts) buzz moderation ban --user <admin-pubkey> # run as the community owner's key
Defensive patterns
Strategy: validation
Validate before calling
-- Pre-flight for admin-driven ban/timeout: resolve the target's role first
SELECT role FROM relay_members WHERE pubkey = :target;
-- if role IN ('owner','admin'), route the action to the community owner instead Type guard
fn admin_may_action(action: ModerationAction, target_role: Option<&str>) -> bool {
match target_role {
Some("owner") | Some("admin") => !matches!(
action,
ModerationAction::Ban | ModerationAction::Timeout
),
_ => true,
}
} Prevention
- Check the target's role with a users/members lookup before offering ban/timeout in admin tooling
- Automated moderation bots should run as plain moderators, not admins, to avoid hitting the owner/admin seam entirely
When it happens
Trigger: A kind moderation command (ban or timeout) issued by an actor whose relay_members role is admin, where the target's relay_members role is owner or admin; authorize_moderation_action returns this bail before any state change.
Common situations: Two admins in a community and one tries to moderate the other; an admin bot auto-banning spam tries to ban a promoted admin account; confusion over who is allowed to moderate whom after role changes.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- actor is not an active member
- build_ref_state_event
- BUZZ_RELAY_PRIVATE_KEY must be set when…
- failed to sign member snapshot
- invalid role
AI-assisted analysis of block/buzz@934f3325c3 (2026-08-20).
Data as JSON: /api/errors/2cc747300acfd301.
Report an issue: GitHub.
Appendix: source
Thrown at crates/buzz-relay/src/handlers/moderation_authz.rs:167
channel_role: Option<&str>,
action: ModerationAction,
) -> anyhow::Result<ModerationAuthority> {
match actor_role {
// Owner holds every capability, community-wide, with no guard rail.
Some("owner") => Ok(ModerationAuthority::CommunityOwner),
// Admin holds every capability, but cannot ban/timeout the owner or a
// fellow admin — only the owner may action an admin. The guard trips only
// on a target *role* of owner/admin: a target with no `relay_members` row
// (a drive-by spammer who already left) is bannable. Unban/Untimeout lift
// a restriction and are intentionally unguarded at this role seam. The
// command handler separately rejects a banned actor on every transport,
// so the reachable case is an unrestricted admin lifting another admin's
// restriction; that remains benign, audited, and owner-reversible.
Some("admin") => {
if matches!(action, ModerationAction::Ban | ModerationAction::Timeout)
&& matches!(target_role, Some("owner") | Some("admin"))
{
anyhow::bail!("an admin cannot ban or time out a community owner or fellow admin");
}
Ok(ModerationAuthority::CommunityAdmin)
}
// Not a community owner/admin: channel owner/admin keep channel-local
// authority for DeleteMessage/Kick only.
_ => match (action, channel_role) {
(
ModerationAction::DeleteMessage | ModerationAction::Kick,
Some("owner") | Some("admin"),
) => Ok(ModerationAuthority::ChannelRole),
_ => anyhow::bail!("moderator access required"),
},
}
}
#[cfg(test)]
mod tests {
use super::*;View on GitHub (pinned to 934f3325c3)