Skip to content

Self-join (kind:9021) hard-codes role=member, making a self-joined agent permanently unmentionable #6561

Description

@rmichelena

Summary

handle_join_request (kind:9021) hard-codes MemberRole::Member. An agent that joins a channel itself is therefore recorded as a human member — and because clients use the channel role as the "is this an agent" signal, that agent becomes invisible to @mention autocomplete, with no in-app way to repair it.

The same channel reached through a different entry point works fine, which is what makes this hard to spot.

Three entry paths, three different role decisions

Entry path How the role is decided
Desktop invite role: user.isAgent ? "bot" : "member"desktop/src/features/channels/ui/MembersSidebar.tsx
kind:9000 / add-member handle_put_user reads a role tag; falls back to Member only for a brand-new member
kind:9021 / self-join hard-coded MemberRole::Member; the joiner cannot express a role

kind:9021 carries no role tag and buzz channels join exposes no --role, so this is not a client omission — there is no way to ask for anything else.

Why Member is the wrong default here specifically

MemberRole's own documentation says Bot is not a lower rung:

// crates/buzz-core/src/channel.rs
/// The hierarchy for permission checks is: Owner > Admin > Member > Guest.
/// Bot is a **separate designation** — it is not part of the linear hierarchy.

So assigning Member to an agent is not a conservative privilege choice — it is a category error, and it is the category clients read.

Impact

  1. Agent self-joins an open channel (the only channels where 9021 is accepted).
  2. It lands as member.
  3. Desktop's mention eligibility requires isAgent === true || member.role === "bot", so it never appears in @ autocomplete.
  4. Repair requires role: bot on a kind:9000, which the relay restricts to owner/admin (only owners/admins may change an active member's role) — and the Desktop role menu offers only admin/member/guest, and hides itself entirely for members already marked bot. There is no in-app path back.

Observed on a self-hosted relay: an agent self-joined at T, was unmentionable for the entire session, and only recovered after an owner-signed add-member --role bot.

Suggested direction

The relay already defines an agent discriminator and relies on it elsewhere:

// crates/buzz-db/src/usage.rs
/// Agent discriminator: `agent_owner_pubkey IS NOT NULL`.

Using that same discriminator in handle_join_request fixes the self-join path without touching handle_put_user, so explicit admin/member/guest assignment for humans is unaffected. PR to follow.

Alternatives, if maintainers prefer a different shape: accept an optional role tag on kind:9021, or have clients repair the role after joining. Both seem worse — the first lets a joiner self-assign, the second spreads the decision across clients that already disagree.

Note on closed relays

agent_owner_pubkey is exactly the field that stays NULL on relays with require_relay_membership = true (#5581). Where that is unresolved, the discriminator reports every agent as a human and this fix is a no-op — so the two are the same knot, and #5581 is a prerequisite for closed deployments rather than a separate concern.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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