Skip to content

set_member_banned unconditionally publishes a MANAGE_ROLES-gated Grant-strip — readers reject it and flag legitimate bans as suspicious activity #81

Description

@derekross

Summary

First: vector-core 0.5.0's citation fix works — verified live today. A bot-issued ban's banlist edition and refounding are now honored by Armada, and the community survives the read-cut with all members advancing together. Thank you for cutting the release (#80).

This surfaced the next-layer bug: set_member_banned unconditionally publishes the ban's Grant-strip edition (grant_roles(target, vec![])) regardless of whether the acting identity holds MANAGE_ROLES. Grant editions (vsk 3) are reader-gated by MANAGE_ROLES + outrank — so a moderator holding BAN but not MANAGE_ROLES (a completely normal moderation-bot grant) emits a guaranteed-rejected edition at the target's Grant coordinate on every ban.

Compliant readers drop it, and Armada's suspicious-activity detector then flags the ban — an unauthorized edition at someone's Grant coordinate is exactly the shape of a privilege-manipulation attempt. Routine automated moderation reads as an incident.

Code path (present in 0.5.0 and on master @ 2152459)

crates/vector-core/src/lib.rs (set_member_banned, ~line 2971):

crate::community::v2::service::set_banlist(&transport, &community, &list).await…?;   // BAN-gated: accepted
if banned {
    crate::community::v2::service::grant_roles(&transport, &community, &pk, vec![]).await…?;  // MANAGE_ROLES-gated: rejected
}crate::community::v2::service::refound_community(&transport, &community, &[pk]).await…?;      // BAN-gated: accepted

grant_roles (v2/service.rs:3157) publishes with no local authority pre-check — contrast edit_channel_metadata, which mirrors its reader gate locally via ensure_channel_manager precisely so an unauthorized device doesn't "advance its own edition floor onto a head every reader rejects" (its own comment). The Grant path has no such mirror. Note that floor-wedging concern applies here too: the rejected strip advances the publisher's local floor at the target's Grant entity.

Observed live (2026-08-02)

v2 community, human owner, bot granted a Moderator role with permissions 568 = KICK | BAN | MANAGE_MESSAGES | MENTION_EVERYONE (no MANAGE_ROLES, bit 0). Bot bans a spammer via Member::ban():

  • banlist → applied in Armada's audit log
  • grant strip → "not applied" + suspicious-activity banner
  • refounding → applied; community intact

Every automod ban reproduces this: the ban is fully effective, but the audit surface reports a rejected authority action and raises an alarm.

Why the rejected strip costs nothing to skip

Reader folds already drop a banned member's authority editions once the banlist edition is honored (CORD-04 §4 — Armada tests this explicitly: "drops a banned npub's authority editions, banlist aside"). The explicit strip is belt-and-braces; when the actor can't authorize it, publishing it buys nothing and produces the false alarm.

Suggested fix

Either:

  1. Mirror the reader gate locally (the ensure_channel_manager pattern): skip the Grant-strip phase when the local authority view says the actor lacks MANAGE_ROLES (or lacks outrank), relying on the §4 auto-drop. Least invasive; keeps readers strict.
  2. Or, at the spec level, define the in-ban Grant strip as authorized under BAN and update reader folds to accept it in that context.

(1) seems clearly preferable — it's a two-line guard and changes no wire semantics.

Related

Armada-side display report (stale-plane rendering + the suspicious-activity UX around this) filed on the Armada Nostr repo: event 18cff094508a0ef191f9845b4f2b40ff7f94b240aaa478f052fd4061ad236f7c (gitworkshop.dev).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions