Repository navigation
feat: channel-wide mentions β @channel and @hereΒ #3146
Description
π€ Filed by kray's AI agent on his behalf.
Problem
Getting the attention of everyone in a channel means typing every member's name β and keeping that habit current as membership changes. The real cost isn't the typing, it's the silent under-notification: as a community member described on #2728, the moment a fourth agent joined their three-agent channel, "every saved snippet and habit is silently stale β he gets three answers and never notices the fourth was left out."
User groups (#2728) solve the named-set case, but someone has to create and maintain the group. Quoting the same comment: a zero-config @channel-style mention "is the 80% case and has no membership to keep in sync." #2332 asks for the same primitive from the agent side β "will also be good to have an 'everyone' tag or similar."
And sometimes you don't want everyone β you want whoever is around right now. Asking a 40-person channel "can someone restart the deploy?" at 2am shouldn't badge 35 sleeping people.
Slack users reach for @channel and @here by muscle memory. Buzz has no equivalent β today a literal @channel renders as plain text and notifies nobody.
Proposal
Two reserved, zero-config mention tokens with Slack-parity semantics:
@channelβ notify every member of the channel.@hereβ notify only members who are online right now.
Wire format: one marker tag, not roster fan-out
The message carries a single ["notify", "channel" | "here"] marker tag (exact tag name finalized in the PR). It does not expand into per-member p tags, deliberately departing from the compose-time-expansion idiom used by team mentions (#1918), category mentions (#2531), and user groups (#2791):
@hereis not expressible as a compose-time list. "Who is online" is a delivery-time fact; a frozen send-time snapshot is stale against the presence TTL and leaks exactly who was idle to the sender.- Roster fan-out breaks at scale. N
ptags bloat the signed event, leak the full roster, hit the 50-mention cap, and are silently suppressed by NIP-PL'ssuppress.p_tags_maxhellthread gate β the push spec's own example value is 20, so@channelpush would silently vanish in any channel larger than that. - Agent amplification. Every
ptag wakes an ACP-managed agent (event_mentions_agent), so an expanded@channelin an agent-heavy channel burns one model turn per agent, involuntarily. - Precedent exists on both sides. Buzz already ships a p-tag-free channel-wide attention signal β
["broadcast", "1"](NIP-CW) β that the notification pipeline understands today. Matrix faced this exact fork and chose the structured flag (m.mentions: {"room": true}, MSC3952) over per-user fan-out.
Foreign Nostr clients degrade gracefully (NIP-27 style): they render literal @channel text and simply don't notify. No upstream NIP defines group-wide mentions (verified against nostr-protocol/nips and the NIP-29 client ecosystem), so this will be specced as a Buzz-local NIP in docs/nips/ (working name NIP-CM, Channel Mentions).
Notification semantics
@channelescalates to mention-tier (numeric badge, mention feed, OS notification) for every channel member.@hereescalates live-only: clients that receive the event while online treat it as a mention; members who were offline see a normal message later β no retroactive badge (Slack parity).- Channel mutes suppress
@channel/@hereβ unlike a direct@namemention, which continues to pierce mutes (Slack parity; same call Add channel-wide @everyone and @here mentionsΒ #2138 made). @channelmessages appear in members' Home mention feed via a relay-side lookup β one row per event, joined against membership at query time, not oneevent_mentionsrow per member.- Editing a message to add
@channeldoes not re-notify.
Composer
Autocomplete rows for @channel ("Notify all N members") and @here ("Notify N online members"), plus a confirmation prompt before sending β reusing the existing send-flow interception point (same seam as the non-member mention dialog).
CLI and automation safety
buzz messages send gets an explicit --notify channel|here flag. A literal @channel typed in content without the flag warns and sends without the tag β agents and workflows must opt in explicitly to mass-notify, so a template variable can never accidentally ping 400 people. (Same spirit as the unresolved-mention warning in #3047.)
Agents: humans-only, by design
@channel and @here notify humans only. The marker tag carries no p tags, so event_mentions_agent never fires and no managed agent burns a model turn β an involuntary N-agent turn storm per @channel would be a real cost explosion in agent-heavy channels. Channel-wide agent addressing is a different intent and already has a lane: @agents category mentions (#2531) and user groups (#2728) both expand to explicit p tags and wake agents deliberately. If demand emerges for agent-inclusive broadcast tokens, they'd be additional reserved tokens on this same marker mechanism β a follow-up, not v1.
Permissions and abuse
- v1: any channel member may use
@channel/@here(Slack's default). Relay ingest validates the tag (valid mode, allowed kinds, not in DMs). Per-channel restriction ("only admins may @channel") is a natural follow-up on the feat(relay): per-channel write policy for announcement / read-only channelsΒ #2497 per-channel write-policy surface. channelandherebecome reserved mention tokens: resolved ahead of display-name matching (a member named "here" can't shadow or be shadowed), and reserved in the user-group handle validator from feat: user groups β mentionable groups of people and agentsΒ #2728.
Prior art
- Add channel-wide @everyone and @here mentionsΒ #2138 β a full-stack
@everyone/@hereattempt (closed unmerged by its author, July 2026; never reviewed). It chose compose-time p-tag expansion with a 4,000-recipient cap; this proposal diverges for the reasons above but reuses several of its calls (mute-respecting semantics, relay-side tag validation, reserved-token autocomplete copy). It also surfaced a real bug this feature depends on: the hidden 1,000-rowLIMITinget_members()truncating rosters β being fixed separately. - Agents not replying unless taggedΒ #2332 β the open ask for an "everyone tag". Its agent-wake angle is intentionally not covered here (
@channelis humans-only); the agent side is served by@agentscategory mentions (feat(desktop): add @agents/@people category mentionsΒ #2531) and user groups (feat: user groups β mentionable groups of people and agentsΒ #2728). - feat: user groups β mentionable groups of people and agentsΒ #2728 / feat: user groups β mentionable member sets across relay, CLI, and desktopΒ #2791 β user groups: the named-set complement to this zero-config alias; the
["group", id, handle]marker precedent. - feat(desktop): add @agents/@people category mentionsΒ #2531 β
@agents/@peoplecategory mentions (open): adjacent composer work; this PR will coordinate with itsmentionCandidates.tshelpers. - feat(relay): per-channel write policy for announcement / read-only channelsΒ #2497 β announcement channels: the permission axis this feature's follow-up gate would build on.
Out of scope / follow-ups
- Mobile compose + pill rendering (follow-up issue, mirroring Mobile: user groups β mention compose, group management, and pill renderingΒ #2792; mobile already surfaces the mention feed).
- Per-channel
@channelpermission policy (with feat(relay): per-channel write policy for announcement / read-only channelsΒ #2497). - A dedicated NIP-PL push class for channel mentions (v1 rides existing channel-wide lease subscriptions).
@everyoneas a community-wide token β deliberately not proposed; channel scope only.
Activity
feat(relay): accept @channel/@here notify tag and feed @channel mentiβ¦
fix(relay): make the mentions UNION dedupe on event columns alone (blβ¦
docs(test): note deferred execution of the NIP-CM e2e suite (block#3146)
feat(cli): add --notify channel/here to messages send (block#3146)
feat(sdk,cli): channel-wide mention support for @channel/@here (blockβ¦
feat(desktop): compose and send @channel/@here channel mentions (blocβ¦
feat(desktop): render and notify for @channel/@here mentions (block#3146
docs(nips): add NIP-CM channel mentions spec (block#3146)
docs(nips): correct @here persistence and NIP-PL claims in NIP-CM (blβ¦
test(relay): drop the stale deferred-execution note on the NIP-CM e2eβ¦
test(desktop): add PR screenshot spec for @channel/@here mentions (blβ¦
fix(desktop): let direct mentions pierce channel mutes on both feed pβ¦
fix(relay): require channel membership to send @channel or @here (bloβ¦
fix(desktop): carry notify tags through the edit tag overlay (block#3146
fix(relay): validate the notify tag before every early-return handler (β¦
fix(desktop): dedupe live events delivered by two channel subscriptioβ¦
fix(relay): gate the notify tag on WebSocket ephemeral events (block#β¦
docs(nips): clarify that @here live-only means observation-time (blocβ¦
docs(db): record the single-index trade-off on the NIP-CM feed branch (β¦
fix(sdk): mask tilde fences and multi-backtick spans when scanning meβ¦
docs(desktop): clarify that @here live-only is observation-time (blocβ¦
fix(db): register channel_notifications in the desired-state schema (bβ¦
test(db): drop count/index fragility from the embedded-migrator test (bβ¦
fix(desktop,cli): request the bounded mentions feed for Inbox and feeβ¦
fix(relay,cli,desktop): review round-2 β category label, CLI defaultsβ¦
davidglogan commentedon Jul 31, 2026
+1 β came looking for exactly this tonight (wanted a way to ping everyone in a channel, similar to Slack's
@here) and found this already filed. Glad to see it's in progress along with the mobile (#3198) and inactive-community-rail (#3235) follow-ons.ratho13 commentedon Aug 2, 2026
Strong proposal β the marker-tag wire format in particular looks right. We put a
39-agent workspace into production today, so here's a data point from the far end
of the scale, plus a workaround that works until this ships.
Our channels map to legal entities, so
#baltic-ihubhas 16 personas in it. (Theteam is public if anyone's curious about the shape of it:
https://baltic-ihub.com/team.) The under-notification problem you describe is
exactly what we'd run into as membership shifts.
A second cost, specific to agents
The original post frames the cost of over-notification as attention, which is
precisely right for people. For agents there's a second cost stacked on top:
waking one doesn't just badge it, it starts an LLM session. Sixteen agents means
sixteen sessions, each re-sending its system prompt, each billing tokens. Our
persona prompts average ~700 tokens and reach 2,700 for the larger roles, so a
single channel-wide mention in our biggest room is a four-figure token event
before anything useful has been said.
That's an argument for this feature with good scope controls, not against it β
and
@herealready points in the right direction. Two things we'd find valuablealongside it:
mentions. Our compliance reviewer doesn't need to spin up because the room is
sorting out lunch β whereas the front-office personas absolutely should.
@heremight mean "idle" rather than "online". Agents areonline by construction, so presence carries little signal; readiness to take
work carries a lot. Possibly out of scope here, but worth noting before
presence semantics get pinned down.
Workaround for anyone who needs this today
buzz-acpcan already approximate@channelthrough subscription rules, with norelay changes. In
buzz-acp.toml:Run with
--subscribe config --config <path>. Any literal works as the trigger βours is German because the team is, which conveniently avoids colliding with a
real
@channellater.Three caveats before anyone copies it:
quotes and code blocks. A message quoting this comment would wake the room.
@hereequivalent β there's no presence data in the filter context. Theavailable bindings are
content,author,channel_id,kind,timestamp,plus
str_contains/str_starts_with.A small adjacent note
Not this issue, but it may save someone the debugging time it cost us today:
--respond-to anyonedeliberately does not apply inside DMs (lib.rs, "DMhardening: fail-closed to DM"). Without
BUZZ_ACP_AGENT_OWNERset, every DM to anagent is dropped. The behaviour is correct and a good default β the only rough
edge is that at
RUST_LOG=infothe drop is invisible;inbound author gate β dropping eventonly appears at debug level. A one-line hint in the buzz-acpREADME would probably be enough.
Happy to test a preview build against 39 live agents if that's useful.