Proof of concept mechanical port of ircnet/ircd to Rust as part of a bit about C being insecure for network services
0

Configure Feed

Select the types of activity you want to include in your feed.

ircd.rs / docs / superpowers / plans / 2026-06-10-p11-slice64-bot-tag.md
4.7 kB

P11 slice 64 — IRCv3 bot message tag#

Date: 2026-06-10 Track: IRCv3 (the thin follow-on that lights up slice 55's deferred bot tag, exactly as slice 62 server-time consumed the message-tags delivery plane). Spec: docs/rfcs/ircv3/bot-mode.md (§ "The bot tag").

Scope#

When a +B (bot-mode) user sends a message, the server adds a valueless bot message tag to the delivered line, only for recipients that negotiated message-tags. Scoped — exactly like slice 62 — to the message delivery plane: PRIVMSG / NOTICE / TAGMSG (the fan-out to recipients and the slice-61 echo-message copy back to the sender).

Slice 55 shipped +B mode + ISUPPORT BOT=B + WHOIS 335 + WHO B flag, and explicitly deferred the tag because no tag-rendering plane existed yet. Slices 56/58 built that plane; this slice spends it.

Locked decisions#

  • No new capability. The bot tag is not negotiated by the receiver — it is gated by the existing message-tags cap (per the spec: "MUST only be sent to users who have requested the message-tags capability") and the sender's +B mode. So cap.rs / ClientCaps / SUPPORTED are untouched — this is a pure delivery-plane change. This is the key contrast with slices 59–63, which each added a cap.
  • Valueless. Servers MUST NOT send a value; clients MUST ignore one. We emit a bare bot tag (("bot", "")).
  • Sender-driven, not recipient-driven. Whether the tag appears depends on the sender being a bot; whether it's delivered depends on the recipient's message-tags. The two are orthogonal and both must hold.
  • Tag ordering. The bot tag is a server tag → prepended before client-only (+) tags, like time. When both server-time and the bot tag apply, order is @time=…;bot;+clienttags… (time first, then bot, then client tags) — deterministic, spec-order-agnostic between the two server tags.

Design#

Site Change
message.rs New Message::with_bot_tag(&self) -> Message — clones, inserts ("bot","") at index 0 (mirrors with_server_time).
command/message.rs Compute sender_is_bot = client.modes & UserMode::Bot.bit() != 0 once. The per-recipient render(caps) gains a bot = sender_is_bot && caps.message_tags arm: a 2×2 match over (bot, server_time) so the no-tag common path still avoids a clone.
command/tagmsg.rs Same sender_is_bot + bot = sender_is_bot && caps.message_tags in render. (Fan-out recipients are all message-tags consumers by the gate; the echo correctly gates on the sender's own cap.)

S2S relay paths are unchanged: a remote member's own server stamps the bot tag from the network-propagated +B (slice 55 already rides +B over UNICK/UMODE) — the same single-server local-mailbox stance slices 59–63 take.

Divergences (documented)#

  • PRIVMSG/NOTICE/TAGMSG only this slice. The spec's "all commands … (JOIN, MODE, NOTICE, and all others)" and "numerics directly caused by the bot" are deferred — each a thin follow-on now that with_bot_tag exists, mirroring slice 62's scope.
  • Remote members get no bot tag from this server (their server stamps it).
  • No value ever; numeric replies untagged.

Tests (TDD, inverse invariants first)#

  • message.rs unit: with_bot_tag inserts a valueless bot at the front; composes with with_server_time as time, bot, +client.
  • command/message.rs units: a bot's channel PRIVMSG → a message-tags consumer sees @bot; inverse a non-consumer co-member sees none; inverse a non-bot sender → the consumer sees none; bot tag + a client +tag coexist (bot before +); a bot's NOTICE to a local-nick consumer is tagged; the echo to a bot sender carries @bot iff the sender negotiated message-tags (+ the tagless inverse).
  • command/tagmsg.rs units: a bot's TAGMSG to a consumer carries @bot; inverse a non-bot sender's TAGMSG to the same consumer does not.
  • Proptest bot_tag_proptest.rs (fuzz target): drive PRIVMSG through a real Session with arbitrary (sender_is_bot, recipient_has_message_tags, text) — the delivered line carries a parseable valueless bot tag iff both bools hold, never otherwise; the tag never has a value; panic-freedom on arbitrary trailing bytes.
  • Golden golden_bot_tag.rs: real binary — alice does MODE alice +B; dan (message-tags ON) sees @bot on alice's channel line; mel (OFF) sees no tag. Valueless tag is fully deterministic → no new canonicalizer rule.

Gate#

cargo test -p leveva green; cargo clippy -p leveva --tests clean; cargo build --workspace 0 warnings.