IRCv3 Support
Comprehensive implementation status of IRC protocol features in repartee, covering RFC 2812 core protocol and IRCv3 capability extensions.
Capability Negotiation
repartee implements the full IRCv3 capability negotiation framework including CAP LS 302 with multiline parsing, CAP REQ/ACK/NAK, and CAP NEW/DEL for runtime capability changes.
| Feature | Status | Notes |
|---|
CAP LS 302 | Done | Multiline parsing, field3/field4 handling |
CAP REQ/ACK/NAK | Done | Request + detect acceptance |
CAP END | Done | Properly closes negotiation |
CAP NEW/DEL | Done | Runtime capability changes via cap-notify |
Tier 1 -- Must Have
| Capability | Status | Spec | Notes |
|---|
multi-prefix | Done | 3.1 | All mode prefixes per user in NAMES |
extended-join | Done | 3.1 | JOIN includes account + realname |
ircnet.com/extended-join | Done | -- | IRCnet ircd 2.12 vendor extension: JOIN also carries <uid> <ip> <netjoin>. Takes precedence when both variants are acked |
server-time | Done | 3.2 | @time tag used as message timestamp |
account-tag | Done | 3.2 | User account in message tags |
cap-notify | Done | 3.2 | Server-side capability change notifications |
away-notify | Done | 3.1 | Real-time away status changes |
account-notify | Done | 3.1 | Account login/logout notifications |
chghost | Done | 3.2 | Real-time ident/hostname changes |
| SASL EXTERNAL | Done | 3.1 | CertFP-based authentication via client TLS certificate (client_cert_path PEM chain + key) |
| SASL ECDSA-NIST256P-CHALLENGE | Done | -- | NIST P-256 public-key challenge-response |
| SASL SCRAM-SHA-512 / 256 / 1 | Done | 3.1 | RFC 5802/7677 salted challenge-response with server signature verification |
| SASL OAUTHBEARER | Done | -- | RFC 7628 OAuth 2.0 bearer token authentication (explicit opt-in via sasl_mechanism = "OAUTHBEARER") |
| SASL mechanism selection | Done | -- | Auto-detect strongest: EXTERNAL > ECDSA-NIST256P-CHALLENGE > SCRAM-SHA-512 > SCRAM-SHA-256 > SCRAM-SHA-1 > PLAIN |
Tier 2 -- High Value
| Capability | Status | Spec | Notes |
|---|
echo-message | Done | 3.2 | Server echoes own messages back; local echo suppressed |
labeled-response | Done | 3.2 | Request/response correlation (@label) for commands and batches (ACK, BATCH) |
invite-notify | Done | 3.2 | Channel members see invites |
batch | Done | 3.2 | NETSPLIT/NETJOIN summary messages; carries multiline, chathistory, search, and labeled-response batches |
userhost-in-names | Done | 3.2 | nick!user@host parsing in NAMES |
no-implicit-names | Done | -- | Suppresses automatic NAMES on JOIN (no-implicit-names, draft/no-implicit-names, soju.im/no-implicit-names) |
message-tags | Done | 3.2 | Tags extracted, stored in buffer and DB; also carries client tags on TAGMSG |
draft/chathistory | Done | -- | Server/bouncer history: TARGETS, scroll-back paging, reconnect gap-fill, BETWEEN, AROUND (see below) |
draft/event-playback | Done | -- | Requests JOIN/PART/mode replay inside CHATHISTORY batches |
draft/multiline | Done | -- | Long/paste messages framed as one BATCH; inbound reassembly (see below) |
draft/read-marker | Done | -- | Cross-client read state synchronization via MARKREAD (draft/read-marker and soju.im/read) |
draft/message-redaction | Done | -- | Message deletion (REDACT) via /redact <target> <msgid> [reason] |
draft/account-registration | Done | -- | In-band REGISTER and VERIFY flows via /account |
draft/metadata-2 | Done | -- | Conversation metadata (pin, mute, block) and subscriptions via /bmeta |
draft/extended-isupport | Done | -- | Extended RPL_ISUPPORT parameter negotiation (ISUPPORT) |
draft/channel-context | Done | -- | Channel context client tag (+draft/channel-context / channel-context) |
+typing client tag | Done | -- | Ratified typing indicator on TAGMSG. Send + receive, status-line item in TUI and web UI (see below) |
Tier 3 & Bouncer Extensions
| Capability | Status | Notes |
|---|
msgid | Done | Read from message-tags; drives CHATHISTORY deduplication, redaction, and reconnect anchors |
monitor / extended-monitor | Done | Online/offline nick watching via /monitor (add, remove, clear, list, status, show) plus away/account/host/realname notifications (extended-monitor / draft/extended-monitor) |
setname | Done | Real-time realname changes (SETNAME via /setname <real name>) |
draft/webpush / soju.im/webpush | Done | Browser WebPush VAPID key retrieval and subscription lifecycle over IRC |
soju.im/bouncer-networks | Done | Bouncer network discovery (BOUNCER LISTNETWORKS), live updates (soju.im/bouncer-networks-notify), network management (/bouncer), and BOUNCER BIND |
soju.im/search | Done | Server-side full-text message search (SEARCH via /bsearch) |
soju.im/client-cert | Done | Pinning and generating TLS client certificates on Soju accounts (CERTFP via /bcert) |
reply | Not Started | Message threading via +draft/reply |
Server-side history (draft/chathistory)
When the server or bouncer (soju, lurker, ergo, …) advertises draft/chathistory, repartee requests history over the wire — so you no longer have to drop into the read-only log browser to read older messages.
- Conversation discovery (
CHATHISTORY TARGETS) -- on bouncer-backed connections, repartee discovers active channels and direct-message queries automatically on connect and loads their recent backlog (display.backlog_lines). Opening an empty query buffer on a bouncer also fetches its recent history on demand.
- Scroll-back paging (
CHATHISTORY BEFORE) -- scrolling a channel or query toward the top pages older messages and prepends them to the live view (irssi/WeeChat/The Lounge style). In the web UI the scroll position is anchored so loading older pages never makes the view jump, and the viewport auto-fills on a fresh connect so scroll-back is reachable even before the window overflows.
- Reconnect gap-fill (
CHATHISTORY AFTER / LATEST) -- after a reconnect, fills the gap since the last message you saw, so nothing is missed across a blip. Multi-page gaps keep paginating until caught up.
- Bounded ranges & context (
CHATHISTORY BETWEEN / AROUND) -- /bsearch between queries bounded time ranges on servers with draft/chathistory, and /bsearch context <row> fetches surrounding context for any search hit via CHATHISTORY AROUND.
- Storage model -- direct IRC connections fold history into the local SQLite backlog, while bouncer-backed connections keep history server-owned in memory without duplicating messages into local SQLite logs.
- Quiet ingest & deduplication -- history batches are rendered in place without re-triggering notifications or activity counters, and rows are de-duplicated by
msgid (with a millisecond-timestamp fallback for msgid-less servers). draft/event-playback is requested alongside so JOIN/PART/mode changes replay inside the batch.
Multiline messages (draft/multiline)
draft/multiline lets a genuinely multi-line message travel as a single logical message instead of N separate lines.
- Outbound -- a pasted or composed multi-line message is framed as one
BATCH of type draft/multiline, honouring the server's advertised max-bytes/max-lines limits, and paste-coalesced so a paste is one message rather than a flood. This is E2E-safe: an encrypted multi-line message stays a single RPE2E unit.
- Inbound -- incoming multiline batches are reassembled into one message for display and storage; oversized or inconsistent batches are rejected and
FAIL BATCH MULTILINE_* errors are surfaced rather than swallowed.
- TUI compose -- press Alt+Enter to add a line break in the composer and send the whole block as one multiline message.
- Web -- the web composer sends multi-line input as one
\n-preserving message when the cap is active (and still falls back to line-by-line sending when it isn't).
Typing indicators (+typing)
repartee implements the ratified +typing client tag. It rides on TAGMSG and needs only message-tags, so there is no separate capability to negotiate and nothing to configure to get it working.
- States --
active (you are composing), paused (text is still in the input but you have stopped for a moment), and done (the input is empty again). active and paused render identically; the distinction is not surfaced.
- Timing -- notifications are throttled to one every 3 s per target. A received
active expires after 6 s and a paused after 30 s, so a peer that crashes mid-sentence never leaves a stuck indicator. Sending a real message retracts the indicator implicitly -- no done is sent after it.
- What counts as typing -- non-empty text that isn't a
/command. A /me (or /action ) draft is a message, so it does announce typing.
- Send/receive are separate --
[typing] show controls whether you see other people; send_channels and send_queries independently control whether you announce your own, in channels and in private queries respectively. All three default to on.
- Compatibility -- repartee only ever sends the ratified
+typing, but still accepts the pre-ratification +draft/typing on receive. The tag is suppressed on servers that block it via ISUPPORT CLIENTTAGDENY, and is never sent on an unregistered connection.
- Never noisy -- a
TAGMSG never opens a buffer, never lands in your log or scrollback, and never bumps an activity or unread counter. Replayed history batches and your own echo-message reflections are ignored, and ignore-list rules apply. Typing state is dropped on PART, QUIT, KICK, nick change, netsplit and disconnect.
- Flood-safe -- typing frames are charged against the same flood budget as everything else and are skipped when headroom runs low, so an indicator can never get you flooded off.
- Scripting -- hook
api.on("irc.typing", ...) to observe or suppress typing notifications. See the Lua API reference.
The indicator is rendered by the typing status-line item (in the default [statusbar] items list), in both the TUI and the web UI: alice is typing…, alice and bob are typing…, alice, bob and 2 others are typing….
RFC 2812 -- Core Protocol
Connection Registration
| Feature | Status | Notes |
|---|
| PASS | Done | Server password sent before registration |
| NICK | Done | Nickname setting + ERR_NICKNAMEINUSE retry |
| USER | Done | Username/realname registration |
| OPER | Done | IRC operator login |
| QUIT | Done | Disconnect with message |
| USER MODE | Done | +i, +w, etc. |
| SETNAME | Done | Live realname update via setname capability (/setname) |
Channel Operations
| Feature | Status | Notes |
|---|
| JOIN | Done | Multiple channels, keys |
| PART | Done | Leave with reason |
| CHANNEL MODE | Done | +beiklmnostRIv and prefix modes |
| TOPIC | Done | Get/set, RPL_TOPIC/RPL_TOPICWHOTIME |
| NAMES | Done | RPL_NAMREPLY with prefix parsing, multi-prefix, userhost-in-names |
| LIST | Done | Channel listing |
| INVITE | Done | Invite user + notification |
| KICK | Done | Kick with reason |
| REDACT | Done | Message deletion via draft/message-redaction (/redact) |
Messaging & Queries
| Feature | Status | Notes |
|---|
| PRIVMSG | Done | Channel + private, CTCP ACTION |
| NOTICE | Done | Server + user notices |
| TAGMSG | Done | Tag-only message; carries the +typing client tag. Never stored or displayed |
| WHO / WHOX | Done | Basic WHO (352) + WHOX (354) with account tracking; SID/UID-aware on ircu-family and IRCnet (see below) |
| WHOIS | Done | Full multi-line WHOIS display |
| WHOWAS | Done | Offline user lookup |
| MONITOR / ISON | Done | MONITOR (/monitor) + ISON (irc.ison Lua event) nick availability tracking |
| AWAY | Done | Set/unset + RPL_AWAY display + per-network manual /away on bouncers |
| WALLOPS | Done | Wall message display |
CTCP
| Feature | Status | Notes |
|---|
| ACTION | Done | Send + receive |
| VERSION | Done | Auto-response |
| PING | Done | Auto-response |
| TIME | Done | Auto-response |
| FINGER | Done | Auto-response |
| SOURCE | Done | Auto-response |
ISUPPORT (005)
| Feature | Status | Notes |
|---|
| Token collection | Done | Raw tokens stored on Connection (plus draft/extended-isupport) |
| Structured parsing | Done | PREFIX, CHANMODES, NETWORK, STATUSMSG, WHOX, EXTBAN, CASEMAPPING, MONITOR, SAFERATE, UTF8ONLY, CLIENTTAGDENY, CHATHISTORY, draft/ICON, FILEHOST |
| Behavior adaptation | Done | Updated on each RPL_ISUPPORT; NETWORK drives label, SAFERATE adapts flood control, draft/ICON exposes network icon (/server icon), FILEHOST powers /upload |
Custom Extensions
| Feature | Status | Notes |
|---|
| WHOX | Done | Extended WHO with field selectors, token matching, account field. Auto-detect via ISUPPORT WHOX token. Auto-WHO on channel join populates NickEntry. Full parity on ircu-family and IRCnet ircd (see below). |
| Extban | Done | $a:account -- account-based bans/exempts/invites. /ban -a account shorthand. ISUPPORT EXTBAN awareness. |
WHOX on ircu-family and IRCnet
/who -- and the nick-list metadata the automatic join-time WHO feeds -- understands the SID/UID-aware replies these ircds send, so away state, account names, ident and real hostnames populate correctly instead of coming back blank.
- Field selector per lineage -- the portable selector
%tcuihnfar is used everywhere; IRCnet ircd 2.12 (detected by the ISUPPORT IDCHAN token) gets %tcuihnfaUr, adding the U (user UID) field. The extra field is never sent to other ircds, which would silently shift the reply layout.
- Reply layout disambiguated by arity -- a 10-argument
354 is the portable layout, an 11-argument one is IRCnet's (with the UID). An account of "0" means "not logged in".
- Plain
WHO (352) now updates state too -- previously it only printed a line, so on servers without WHOX the join-time WHO was wasted and away/host/ident never landed in the nick list.
- Trailing-parameter fix -- IRCnet sends
:<hop> <sid> <realname> where the RFC says :<hop> <realname>, so a stray 4-character SID used to render glued to the realname. The hopcount is now stripped everywhere and the SID only on IRCnet-lineage servers, both defensively.
Skipped
| Capability | Reason |
|---|
sts | Strict Transport Security -- out of scope for now |
zcrypt | Niche, rarely used |
Note: All new protocol handling includes tests. Message tags propagate to the scripting API and storage layer. The capability negotiation framework is extensible for future additions.