-
Notifications
You must be signed in to change notification settings - Fork 25
feat: IRCv3.2+ capability implementations #84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
MrLenin
wants to merge
121
commits into
evilnet:master
Choose a base branch
from
MrLenin:ircv3.2-upgrade
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Phase 1 - CAP 302 Foundation:
- Add cli_capab_version to track CAP negotiation version (0, 301, 302)
- Parse version parameter in CAP LS (e.g., "CAP LS 302")
- Support capability values for CAP 302+ clients
- Add multi-line CAP LS with '*' continuation for long capability lists
Phase 2 - SASL 3.2 Enhancements:
- Add cap-notify capability (CAP_CAPNOTIFY) with FEAT_CAP_cap_notify
- Advertise SASL mechanisms: sasl=PLAIN,EXTERNAL,OAUTHBEARER
- Allow post-registration AUTHENTICATE for OAuth token refresh:
- Remove IsSASLComplete blocker, add ClearSASLComplete macro
- Reset SASL state (agent, cookie, timer) for new auth attempt
- Send AC (ACCOUNT) after successful reauth for registered users:
- Notify channel members with account-notify capability
- Propagate to other servers using correct format based on
FEAT_EXTENDED_ACCOUNTS setting (R/M subtype vs plain format)
This enables OAUTHBEARER token refresh without new P10 protocol commands -
reuses existing SASL 'S' (Start) subcmd for backwards compatibility.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement the server-time capability (IRCv3.2) that adds @time= tags to messages for clients that request it. Timestamps are ISO 8601 format with millisecond precision. Changes: - Add CAP_SERVERTIME capability to capab.h - Add FEAT_CAP_server_time feature flag (default: TRUE) - Add format_server_time() helper for ISO 8601 timestamps - Update sendcmdto_channel_* functions to build dual message buffers (with and without @time tag) based on client capability - Update sendcmdto_common_channels_* functions similarly Functions updated: - sendcmdto_channel_butserv_butone() - Channel messages - sendcmdto_channel_capab_butserv_butone() - Capability-filtered channel - sendcmdto_common_channels_butone() - Common channel notifications - sendcmdto_common_channels_capab_butone() - Filtered common channels - sendcmdto_channel_butone() - PRIVMSG/NOTICE to channels Example output for server-time clients: @time=2025-12-23T12:30:00.123Z :nick!user@host PRIVMSG #chan :message 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements the IRCv3 echo-message capability which echoes PRIVMSG and NOTICE messages back to the sender. This is useful for clients to confirm message delivery and maintain consistent message display. Changes: - Add CAP_ECHOMSG to capab.h - Add FEAT_CAP_echo_message feature flag (default TRUE) - Register echo-message in capability list in m_cap.c - Modify relay functions in ircd_relay.c to echo back: - relay_channel_message() - channel PRIVMSG - relay_channel_notice() - channel NOTICE - relay_private_message() - private PRIVMSG - relay_private_notice() - private NOTICE Private message echoes include sptr != acptr check to avoid duplicate when messaging self. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements the IRCv3 account-tag capability which includes the sender's account name in message tags (@account=accountname or @account=* for not logged in). Changes: - Add CAP_ACCOUNTTAG to capab.h - Add FEAT_CAP_account_tag feature flag (default TRUE) - Register account-tag in capability list in m_cap.c - Refactor send.c message tag handling: - Add format_message_tags() to build combined @time;@account tags - Add wants_message_tags() helper for capability checks - Rename mb_st to mb_tags for clarity - Update 5 send functions to use combined tag handling The implementation combines server-time and account-tag into a single tag string, sending both to clients that request either capability (per IRCv3 spec, clients ignore unknown tags). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements the IRCv3 chghost capability which notifies clients when a user's hostname or username changes, instead of showing QUIT/JOIN messages. Changes: - Add CAP_CHGHOST capability and FEAT_CAP_chghost feature flag - Add CMD_CHGHOST to msg.h - Add SKIP_CHGHOST flag for send functions to skip chghost clients - Modify hide_hostmask() and unhide_hostmask() in s_user.c to: * Send CHGHOST to clients with the capability * Skip chghost clients when doing QUIT+JOIN workaround - Update send.c to handle SKIP_CHGHOST flag Format: :nick!olduser@old.host CHGHOST newuser new.host 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements the invite-notify capability that notifies channel members when someone is invited to the channel. Changes: - Add CAP_INVITENOTIFY to capab.h - Add FEAT_CAP_invite_notify feature flag (default: TRUE) - Register capability in m_cap.c - Send INVITE notification to channel members with capability in both m_invite() (local) and ms_invite() (server) handlers Format: :inviter!user@host INVITE invitee #channel 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement IRCv3.2 labeled-response capability that allows clients to correlate commands with server responses using @Label tags. Changes: - Add CAP_LABELEDRESP capability and FEAT_CAP_labeled_response feature - Store label per-connection in con_label[64] field - Parse @Label=value from client message tags in parse_client() - Add format_message_tags_for() for recipient-specific tag generation - Add sendcmdto_one_tags() for sending messages with tags - Modify send_reply() to include @Label and @time tags - Update echo-message calls to use sendcmdto_one_tags() The label is cleared at the start of each command and included in all responses to that command. Labels are client-side only (no P10 changes). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement IRCv3.2 batch capability for grouping related server responses. This is required for proper labeled-response support on multi-response commands. Changes: - Add CAP_BATCH capability and FEAT_CAP_batch feature flag - Add batch state fields to connection (con_batch_id, con_batch_seq) - Add MSG_BATCH command definition - Implement send_batch_start() to start a batch with label tag - Implement send_batch_end() to end an active batch - Implement has_active_batch() helper function - Update format_message_tags_for() to use @Batch tag when active - Update send_reply() to use @Batch instead of @Label when batched The batch BATCH +id type message includes @Label tag for labeled-response integration. Messages within a batch use @Batch=id instead of @Label. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement setname capability allowing users to change their realname (GECOS field) mid-session per IRCv3 specification. Changes: - Add CAP_SETNAME capability and FEAT_CAP_setname feature flag - New P10 token: SE (SN was taken by SVSNICK) - New m_setname.c with m_setname() and ms_setname() handlers - P10 format: [USER_NUMERIC] SE :[NEW_REALNAME] - Notify channel members with setname capability - Propagate changes S2S IRCv3 spec: https://ircv3.net/specs/extensions/setname 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 14: Add @bot message tag for users with +B mode Phase 15: Implement standard-replies capability (FAIL/WARN/NOTE) Phase 16: Add msgid tag with unique ID generation Phase 17: Implement TAGMSG command (P10 token: TM) Phase 13a: Add S2S tag parser foundation (backward compatible) Features: - @bot tag added to messages from bot-mode users - send_fail/warn/note() functions for structured error responses - Message IDs: <server_numeric>-<startup_ts>-<counter> format - TAGMSG command for tag-only messages (typing indicators) - S2S parser silently skips @tags prefix for compatibility New files: - ircd/m_tagmsg.c: TAGMSG command handlers New capabilities: - standard-replies (CAP_STANDARDREPLIES) New feature flags: - FEAT_CAP_standard_replies (default: TRUE) - FEAT_MSGID (default: TRUE) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add full support for client-only tags (+typing, +reply, etc.) in TAGMSG: Infrastructure: - Add con_client_tags[512] field to Connection struct for temp storage - Extract client-only tags (prefixed with +) in parse_client() - Add format_message_tags_with_client() for formatting tags to clients - Add sendcmdto_one_client_tags() for user-targeted TAGMSG - Add sendcmdto_channel_client_tags() for channel TAGMSG relay TAGMSG changes: - m_tagmsg: Extract client tags from cli_client_tags(sptr) - m_tagmsg: Use new send functions for local delivery with tags - m_tagmsg: Propagate tags S2S via P10 format: TM @+tag=val #channel - ms_tagmsg: Parse incoming S2S tags from @+tag=val first parameter - ms_tagmsg: Relay to local clients and propagate to other servers P10 Format: NUMERIC TM @+typing=active #channel NUMERIC TM @+typing=active;+reply=msgid ABAAB This enables typing indicators and other client-only tags to work across server boundaries. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…OTICE Add server-to-server message tag support (FEAT_P10_MESSAGE_TAGS): Parser changes (parse.c): - Modified parse_server() to extract @time and @msgid tags from incoming S2S messages and store them in cli_s2s_time() and cli_s2s_msgid() - Tags are preserved during message relay for consistency across network Client storage (client.h): - Added con_s2s_time[32] and con_s2s_msgid[64] fields to Connection struct - Added accessor macros cli_s2s_time() and cli_s2s_msgid() Send functions (send.c): - Added format_s2s_tags() function to generate/preserve @time;@msgid tags - Modified sendcmdto_channel_butone() to include S2S tags in server buffers - Modified sendcmdto_one() to add S2S tags for PRIVMSG/NOTICE to servers Build fixes: - Fixed circular dependency between capab.h and client.h by making capab.h self-contained with its own FLAGSET macros and forward declaration - Renamed MSG_BATCH to MSG_BATCH_CMD to avoid conflict with system socket.h - Fixed m_tagmsg.c function calls to use MSG_TAGMSG instead of CMD_TAGMSG for functions that don't take a token parameter Feature flag: - FEAT_P10_MESSAGE_TAGS (default: FALSE) controls S2S tag propagation - When enabled, all PRIVMSG/NOTICE messages between servers include tags - Tags are preserved from incoming messages or generated fresh if absent 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…netsplit Add server-to-server BATCH coordination for netjoin and netsplit events: - Add ms_batch() handler for BT P10 command (m_batch.c) - Register BATCH command for servers in parse.c - Add S2S batch tracking fields to client.h: - con_s2s_batch_id: active batch ID from server - con_s2s_batch_type: batch type (netjoin, netsplit) - Add send_s2s_batch_start() and send_s2s_batch_end() to send.c - Propagate batch markers to local clients with batch capability - Fix FLAGSET_NBITS redefinition warnings in client.h P10 format: [SERVER] BT +batchid type [server1 server2] # Start batch [SERVER] BT -batchid # End batch Batch types: netjoin, netsplit Future work: Hook into END_OF_BURST and SQUIT handlers to automatically trigger batches during net events. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Automatically send IRCv3 batch markers to local clients when: - Netjoin: Server reconnects (junction detected in m_server.c) - Batch start on SetBurst/SetJunction - Batch end on END_OF_BURST - Netsplit: Server disconnects (exit_client in s_misc.c) - Batch start/end wraps exit_downlinks New functions: - send_netjoin_batch_start/end: Track batch on server struct - send_netsplit_batch_start/end: Use caller-provided batch ID Batch ID stored on struct Server for netjoin (persists across burst). Netsplit uses local variable since it's immediate. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Per IRCv3 spec, all messages inside a batch MUST include the @Batch=id tag. This commit adds: - Global active_network_batch_id tracking for network events - set_active_network_batch() and get_active_network_batch() functions - format_message_tags_with_network_batch() for @Batch tag formatting - Modified sendcmdto_common_channels_butone() to use batch tags for clients with CAP_BATCH capability during network events Netsplit: set_active_network_batch() called before exit_downlinks() Netjoin: set in send_netjoin_batch_start(), cleared in send_netjoin_batch_end() 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add send_fail() calls with CAP_STANDARDREPLIES check to TAGMSG, SETNAME, and AUTHENTICATE error paths. Clients with standard-replies capability receive structured FAIL messages in addition to traditional numerics. Error codes added: - TAGMSG: NEED_MORE_PARAMS, INVALID_TARGET, CANNOT_SEND - SETNAME: DISABLED, NEED_MORE_PARAMS - AUTHENTICATE: TOO_LONG, SASL_FAIL IRCv3 spec: https://ircv3.net/specs/extensions/standard-replies 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add sasl_server_available() check to CAP LS handling. SASL is now only advertised when: - FEAT_SASL_SERVER="*" and at least one server is connected, OR - The specific configured SASL server is connected This prevents clients from attempting SASL authentication when X3/services are not available, improving the connection experience. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add SaslMechanisms global to store mechanism list received from services - Add set_sasl_mechanisms() and get_sasl_mechanisms() functions - Handle SASL * * M :mechanisms broadcast in ms_sasl() - Use dynamic mechanism list in CAP LS 302 instead of hardcoded value - Falls back to static value if no broadcast received This allows X3 to announce which SASL mechanisms it actually supports (PLAIN, EXTERNAL, OAUTHBEARER) based on its configuration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previously, broadcast functions like sendcmdto_common_channels_butone() and sendcmdto_channel_butone() built a single tagged message with all available tags (server-time, account-tag, bot) and sent it to any client that requested any tag capability. This meant a client requesting only server-time would also receive @account tags, and vice versa. While technically compliant (clients must ignore unknown tags per IRCv3 spec), it was wasteful and imprecise. New implementation: - Added format_message_tags_ex() with explicit TAGS_* flags control - Added get_client_tag_flags() to determine which tags each client wants - Updated broadcast functions to use per-capability message buffer cache - Each unique combination of capabilities gets its own cached message - Only tags the client actually requested are included Functions updated: - sendcmdto_common_channels_butone() - sendcmdto_common_channels_capab_butone() - sendcmdto_channel_butserv_butone() - sendcmdto_channel_capab_butserv_butone() - sendcmdto_channel_butone() Performance: Uses lazy caching - message buffers are only built when first needed for a given tag combination, then reused for all clients with the same capabilities. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove unused format_message_tags() and format_message_tags_with_network_batch() functions that were replaced by the per-capability message buffer caching. The new implementation uses format_message_tags_ex() with TAGS_* flags and get_client_tag_flags() to build per-client message buffers. Update wants_message_tags() comment to clarify it's now only used for TAGMSG filtering. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add support for the IRCv3 draft/no-implicit-names extension which allows clients to suppress automatic NAMES replies after JOIN. This reduces bandwidth for mobile clients and clients joining many channels. Changes: - Add CAP_DRAFT_NOIMPLICITNAMES to capab.h - Add FEAT_CAP_draft_no_implicit_names feature flag (default: TRUE) - Add capability to m_cap.c negotiation list - Skip do_names() in m_join.c when capability is negotiated - Skip do_names() in m_svsjoin.c when capability is negotiated Spec: https://ircv3.net/specs/extensions/no-implicit-names 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add support for the IRCv3 draft/extended-isupport extension which allows clients to request ISUPPORT (005) tokens before completing registration. This enables early feature discovery during capability negotiation. Changes: - Add CAP_DRAFT_EXTISUPPORT to capab.h - Add FEAT_CAP_draft_extended_isupport feature flag (default: TRUE) - Add MSG_ISUPPORT/TOK_ISUPPORT/CMD_ISUPPORT to msg.h - Add m_isupport declaration to handlers.h - Create new ircd/m_isupport.c command handler - Register ISUPPORT command in parse.c with MFLG_UNREG - Add m_isupport.c to Makefile.in The handler reuses send_supported() from s_user.c. Requires the draft/extended-isupport capability to be negotiated; otherwise returns ERR_UNKNOWNCOMMAND. Spec: https://ircv3.net/specs/extensions/extended-isupport 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements IRCv3 draft/pre-away extension which allows clients to set their away status before completing connection registration. Useful for bouncers and mobile clients that connect in the background. Changes: - Added CAP_DRAFT_PREAWAY to capab.h - Added FEAT_CAP_draft_pre_away feature flag (default: TRUE) - Added con_pre_away and con_pre_away_msg fields to Connection struct - Added mu_away handler for unregistered clients - Apply pre-away state in register_user() after connection completes - AWAY * sets away without message (hidden connection - not broadcast) - Normal AWAY :message is broadcast to servers after registration Specification: https://ircv3.net/specs/extensions/pre-away 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement the draft/multiline IRCv3 extension which allows clients to send multi-line messages as a single unit, solving the code pasting problem that drives users to Discord/Slack/Matrix. Features: - Capability with dynamic value: draft/multiline=max-bytes=4096,max-lines=24 - Client BATCH command handling for draft/multiline type - Message collection with @Batch= tag interception in PRIVMSG - Support for draft/multiline-concat tag for line joining - Batch delivery to supporting clients with proper tags - Fallback delivery as individual messages for non-supporting clients - Echo-message support for multiline batches - IRCv3 standard-replies (FAIL) for error handling - Configurable limits via MULTILINE_MAX_BYTES and MULTILINE_MAX_LINES Files modified: - include/capab.h: Added CAP_DRAFT_MULTILINE - include/ircd_features.h/c: Added multiline feature flags - include/client.h: Added batch state fields to Connection struct - include/handlers.h: Added m_batch declaration - ircd/m_cap.c: Added capability with dynamic value generation - ircd/parse.c: Added @Batch and draft/multiline-concat tag parsing - ircd/m_batch.c: Added client batch handler and delivery logic - ircd/m_privmsg.c: Added batch interception for multiline messages 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds native WebSocket (RFC 6455) support directly in Nefarious, achieving
feature parity with Ergo, InspIRCd, and UnrealIRCd for browser-based clients.
Implementation details:
- New websocket.c with handshake, frame encode/decode functions
- Supports binary.ircv3.net and text.ircv3.net subprotocols
- Integrates with existing event loop (no threading required)
- Uses OpenSSL for SHA1/Base64 (no new dependencies)
- Works with existing SSL/TLS infrastructure
- Gated by FEAT_DRAFT_WEBSOCKET feature flag (enabled by default)
Configuration:
Port { port = 8080; websocket = yes; ssl = yes; };
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add Makefile dependency rules for files added during the IRCv3 upgrade: - m_batch.o (draft/multiline) - m_isupport.o (draft/extended-isupport) - m_setname.o (setname capability) - m_tagmsg.o (message-tags) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements IRCv3 draft/chathistory extension for message history: - LMDB backend for zero-copy reads and MVCC concurrency - All CHATHISTORY subcommands: LATEST, BEFORE, AFTER, AROUND, BETWEEN, TARGETS - Channel message storage (enabled by default) - Private message storage (opt-in via CHATHISTORY_PRIVATE feature) - Message reference formats: timestamp= and msgid= - Proper batch responses with server-time and msgid tags - ISUPPORT tokens: CHATHISTORY, MSGREFTYPES Configuration: CAP_draft_chathistory = TRUE # Enable capability CHATHISTORY_MAX = 100 # Max messages per query CHATHISTORY_DB = "history" # LMDB database directory CHATHISTORY_PRIVATE = FALSE # Enable DM history Build requires: liblmdb-dev (--with-lmdb or --disable-lmdb) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add REDACT command (P10 token: RD) for message deletion - Integrate with LMDB chathistory for message lookup/deletion - Authorization: own messages (time-limited), chanops, opers - Configurable time windows: REDACT_WINDOW (300s default), REDACT_OPER_WINDOW - Disabled by default (draft spec) - enable with CAP_draft_message_redaction - Propagate to channel members with capability and to other servers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement IRCv3 draft/account-registration extension for direct account registration via IRC protocol. Uses XQUERY/XREPLY as fallback mechanism for backward compatibility with older X3 versions. New commands: - REGISTER <account> <email|*> <password> - Register an account - VERIFY <account> <code> - Verify registration with code - REGREPLY (S2S) - Response from services P10 tokens: RG (REGISTER), VF (VERIFY), RR (REGREPLY) Feature flag CAP_draft_account_registration disabled by default since this is a draft specification. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add batch timeout handling per IRCv3 client-batch specification: - Add con_ml_batch_start timestamp to track when batch started - Add FEAT_CLIENT_BATCH_TIMEOUT (default 30 seconds) - Add check_client_batch_timeout() called from check_pings() - Send FAIL BATCH TIMEOUT when batch exceeds timeout When a client opens a batch and doesn't close it within the timeout, the server sends FAIL BATCH TIMEOUT and discards collected messages. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements comprehensive flood protection for IRCv3 draft/multiline batches to prevent abuse while still rewarding legitimate multiline usage. ## Multiline Lag Discounting Instead of applying fake lag immediately for each PRIVMSG in a batch, lag is accumulated during the batch and applied with a configurable discount when the batch ends. This recognizes that batched messages are transmitted simultaneously per the IRCv3 multiline spec. New features: - MULTILINE_LAG_DISCOUNT (default 50): Percentage of lag applied for DMs - 100 = full lag (no benefit to multiline) - 50 = 50% lag (default - rewards multiline while preventing abuse) - 0 = no lag (dangerous - allows unlimited flooding) - MULTILINE_CHANNEL_LAG_DISCOUNT (default 75): Higher discount for channels since they affect more users - MULTILINE_MAX_LAG (default 30): Cap on accumulated lag to prevent extremely long batches from building massive lag debt - MULTILINE_RECIPIENT_DISCOUNT (default TRUE): If all recipients support draft/multiline (no fallback needed), halve the discount percentage ## Batch Rate Limiting - BATCH_RATE_LIMIT (default 10): Maximum batches per minute per client - Returns FAIL BATCH RATE_LIMIT_EXCEEDED when exceeded ## WebSocket Buffer Limit - WEBSOCKET_RECVQ (default 8192): Configurable recvq for WebSocket clients (typically higher than regular clients since WS frames bundle multiple lines) ## Connection Tracking New fields in struct Connection: - con_ml_lag_accum: Accumulated lag during batch - con_batch_minute: Start of rate limit window - con_batch_count: Batches in current window - con_ml_had_fallback: Whether batch needed recipient fallback 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes a race condition where fed_request could be freed while still referenced by the timer callback. Changes: - Add `response_sent` flag to prevent double-send if timer fires after response was already sent via another code path - Rename `complete_fed_request()` to `send_fed_response()` to clarify that it sends the response but does NOT free the request - Let timer destroy event handle all request cleanup consistently - Check `response_sent` early to avoid processing already-handled requests This prevents potential crashes when federation responses arrive just as the timeout timer fires. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds modern password hashing using OpenSSL 3.0+ EVP_KDF API: - PBKDF2-SHA256: $PBKDF2$iterations$salt$hash - PBKDF2-SHA512: $PBKDF2-SHA512$iterations$salt$hash Parameters: - 100,000 iterations (OWASP 2023 minimum) - 16-byte random salt - Constant-time comparison via CRYPTO_memcmp Compatible with Keycloak credential import format. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Certificate Expiry Tracking: - Add ssl_get_cert_expiry() to extract certificate expiration from X509 - Add cli_sslcliexp field to client struct for storing expiry timestamp - Add FEAT_CERT_EXPIRY_TRACKING feature flag (default: enabled) - Add MARK_SSLCLIEXP P10 mark type for propagating expiry to services - Extract expiry at all TLS handshake points (s_bsd.c, m_starttls.c, ssl.c) - Send expiry via P10 MARK SSLCLIEXP to services (s_user.c, s_serv.c) - Handle incoming SSLCLIEXP mark in m_mark.c - Add IAuth SSLCLIEXP handler in s_auth.c WebSocket Origin Validation: - Add FEAT_WEBSOCKET_ORIGIN feature flag (string, default: empty) - Parse Origin header in WebSocket handshake - Add validate_ws_origin() for pattern matching (exact and wildcard *.domain) - Return HTTP 403 Forbidden for invalid origins - Empty string allows all origins, non-empty restricts to listed patterns 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ture flags Phase 1.3: Standard-replies WARN notification - Add fallback_count tracking in process_multiline_batch() - Send WARN BATCH MULTILINE_FALLBACK to senders when truncation occurs - Uses existing send_warn() function from standard-replies implementation Phase 2: +M user mode for legacy users - Add FLAG_MULTILINE_EXPAND to client.h - Add 'M' to userModeList in s_user.c - Add 'M' to infousermodes for ISUPPORT advertisement - Legacy users with +M receive full expansion (no truncation) Phase 3A preparation: Storage feature flags - FEAT_MULTILINE_STORAGE_ENABLED (default: FALSE) - FEAT_MULTILINE_STORAGE_TTL (default: 3600) - FEAT_MULTILINE_STORAGE_MAX (default: 10000) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add in-memory storage for truncated multiline messages that allows legacy clients to retrieve full content via /join &ml-<msgid>: - ml_storage.h/c: Hash table storage with TTL-based expiry - Store/retrieve multiline content by msgid - Automatic expiry via timer callback (every 5 minutes) - Memory-bounded by FEAT_MULTILINE_STORAGE_MAX entries - Statistics tracking for /stats debug output - m_join.c: Virtual channel interception - Intercepts /join &ml-* before normal channel processing - Delivers stored content as NOTICEs without creating actual channel - Returns ERR_NOSUCHCHANNEL if storage disabled or msgid not found - m_batch.c: Integration with legacy fallback - Store full content when truncating for legacy clients - Provide /join &ml-<msgid> hint in truncation notice - Falls back to HistServ FETCH hint if storage disabled - ircd.c: Timer and initialization - Initialize ml_storage at startup - Register periodic timer for expiry cleanup This completes Phase 3A of the multiline legacy fallback improvements. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previously used a 3-tier graduated truncation approach: - Small batches (≤3 lines): send all, no notice - Medium batches (4-10 lines): truncate, "upgrade client" notice only - Large batches (11+ lines): truncate, notice with retrieval hint This was inconsistent - if we're truncating, users should always know how to retrieve the full content. Simplified to 2-tier approach: - Small batches (≤threshold): send all, no notice - Larger batches (>threshold): truncate to max_lines, retrieval hint 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…e messages New truncation strategy for legacy clients: - Small (1-5 lines): Send all lines, no notice - Medium (6-10 lines): Send 4 lines + "[X more lines - ...]" - Large (11+ lines): No preview, just "[Multiline message (X lines) - ...]" The large batch message format makes it clear this is a complete multiline message that needs to be fetched, rather than a partial preview. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Store the label from BATCH +id so that when multiline truncation occurs, the WARN notification can include the original command's label for proper client-side correlation. Changes: - Add con_ml_label field to Connection struct in client.h - Add send_standard_reply_ex() and send_warn_with_label() to send.c - Save label on BATCH +id, clear on BATCH -id in m_batch.c - Use saved label when sending MULTILINE_FALLBACK WARN 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add send_multiline_fallback() helper to consolidate truncation logic - Implement 4-tier graceful fallback chain: 1. Native chathistory (CHATHISTORY AROUND msgid) 2. HistServ available (/msg HistServ FETCH) 3. Local &ml- storage (zero dependencies) - Make preview budget configurable via FEAT_MULTILINE_LEGACY_MAX_LINES 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
5373a83 to
69404ac
Compare
Thread Pool Infrastructure (Phase 1): - Add thread_pool.c/h with 4 worker threads and self-pipe signaling - Update all event engines to call thread_pool_poll() - Add FEAT_THREAD_POOL_SIZE configuration option Async Password Verification (Phase 2): - Add ircd_crypt_async.c for non-blocking bcrypt/PBKDF2 operations - Callback-based API: ircd_crypt_verify_async() Async OPER Authentication (Phase 4): - Add FLAG_OPER_PENDING to client flags - Modify m_oper.c for async verification with fallback to sync Async Logging (Phase 5): - Add ircd_log_async.c/h with dedicated writer thread - Ring buffer for non-blocking log writes - Add FEAT_ASYNC_LOGGING configuration option (disabled by default) IAUTH Code Quality Improvements: - Fix memory leak in auth_close_unused() (i_version, i_config, i_stats) - Replace strcpy with ircd_strncpy for hostname copy - Add buffer truncation warnings for IAuth lines - Add IAUTH_LIST_MAX (1000) limit for config/stats lists - Add tail pointers for O(1) list append - Add proper strtol validation for client ID and port parsing - Add audit logging for IAuth hostname/IP/username changes - Release pending clients when IAuth disconnects - Implement circuit breaker for IAUTH_REQUIRED DoS protection 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 6a - WEBIRC Async: - Add FLAG_WEBIRC_PENDING client flag with Is/Set/Clear macros - Add find_webirc_conf_by_host() helper for host-only matching - Refactor m_webirc.c with async password verification support - Extract apply_webirc_changes() helper for IP/host rewrite logic - Add webirc_verify_ctx struct and webirc_password_verified() callback Phase 6b - SpoofHost Async: - Add FLAG_SETHOST_PENDING client flag with Is/Set/Clear macros - Add find_shost_conf_by_host() helper for hostmask-only matching - Add async support to both m_sethost() and mo_sethost() handlers - Add sethost_verify_ctx struct and sethost_password_verified() callback - Extract apply_sethost_changes() helper for spoofhost application Both implementations: - Use fd-based client lookup for callback reconnection - Check pending flags to verify same client after async completes - Fall back to synchronous verification if async fails to start - Respect IAuth delegation for WEBIRC before local verification This completes the async rework plan - all ircd_crypt() password verification paths now support non-blocking operation via thread pool. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previously "*" was not explicitly handled, so it would try to match origins against the literal "*" pattern. Now "*" is treated the same as empty/unset - allow all origins including missing Origin headers. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When a client is mid-multiline batch (cli_ml_batch_id is non-empty), allow additional buffer space equal to MULTILINE_MAX_BYTES on top of the normal recvq limit. This prevents "Excess Flood" disconnections when clients legitimately send rapid multiline batch lines. The multiline spec treats BATCH +id ... BATCH -id as a single atomic message, so flood protection should account for the batch as a unit rather than individual lines. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- m_batch.c: Store multiline messages to LMDB history with base msgid for CHATHISTORY retrieval. Added debug logging for troubleshooting. - s_bsd.c: Fix excess flood protection to check CAP_DRAFT_MULTILINE capability, not just active batch state. When entire batch arrives in one TCP burst, the batch ID isn't set yet. - m_rename.c: Add missing include for struct Membership iteration - m_account.c: Extended account handling improvements - channel.h: Add multiline history helper declarations - Makefile.in: Proper error propagation in make test Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add log_write calls to history_msgid_to_timestamp to trace why multiline msgid lookups are failing despite successful storage. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements base64 encoding for chathistory responses that contain newlines or exceed P10's ~400 byte content limit: Protocol extension: - CH R: Normal response (content as-is) - CH B: Base64 encoded response with chunking support - First chunk: B <reqid> <msgid> <ts> <type> <sender> <acct> [+] :<b64> - Continue: B <reqid> <msgid> [+] :<b64> - Final: B <reqid> <msgid> :<b64> Receiving side (federation): - ChunkEntry structure tracks multi-chunk messages - Accumulates base64 chunks until final marker - Decodes and adds to federated results Also includes: - History msgid lookup improvements - Multiline content handling in chathistory delivery Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Changed multiline separator from \n to \x1F (Unit Separator) in m_batch.c to avoid base64 encoding overhead in P10 federation - Updated m_chathistory.c to handle both \x1F (new) and \n (legacy) data: - ch_needs_encoding() still triggers base64 for \n (legacy compatibility) - send_history_message() splits on \x1F for client delivery - Added 3-tier fallback for multiline chathistory delivery: - Tier 1: Client has multiline cap → nested draft/multiline batch - Tier 2: Client has chathistory batch only → separate PRIVMSGs with shared msgid - Tier 3: No batch support → truncate to first line - Skip U-lined servers (services) in chathistory federation since they don't store chat history, fixing empty batch timeout issue Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When a client sends a multiline batch with a label tag and has labeled-response capability enabled, the echoed BATCH +id message should include the original label. This allows clients to correlate the server's echo response with their original request. Per IRCv3 labeled-response spec: "The label tag MUST be included in any response to a labeled message". Note: S2S relay via P10 doesn't carry labels (they're C2S/S2C only). Recipients don't receive labels (they didn't send the original). PM echo sends individual PRIVMSGs not BATCH, so no label there. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
f099834 to
b122ae5
Compare
- Changed max-value-bytes from 1024 to 300 (IRC messages limited to 512 bytes) - Changed error code from VALUE_TOO_LONG to VALUE_INVALID per IRCv3 spec - Added visibility checks for private metadata in GET/LIST commands - Added RPL_METADATAEND (762) numeric for proper METADATA LIST termination - Added chathistory retention advertisement in CAP LS (retention=Xd) - Excluded *.o and *.a files from Docker context to prevent stale builds Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When X3 responds with an error for non-existent accounts/channels, immediately send FAIL TARGET_INVALID to the waiting client instead of letting the request timeout after 30 seconds. Protocol: Services send `MD <target> * ! :NOTARGET` where `!` is the error visibility marker. - Added METADATA_VIS_ERROR constant - ms_metadata() parses `!` visibility and handles error response - metadata_handle_response() sends FAIL TARGET_INVALID for errors Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The metadata_x3_is_available() check was preventing MDQ queries from being sent when X3 was connected but hadn't sent any MD messages yet. The heartbeat flag was only set when receiving MD from X3, which doesn't happen when querying for non-existent accounts. The find_services_server() check is sufficient and authoritative for determining if X3 is available to handle metadata queries. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previously, METADATA GET for non-existent channels would only check if the channel existed in memory, and skip querying X3/LMDB for registered channel metadata. This caused the code to fall through to RPL_KEYNOTSET instead of querying X3. Now non-existent channels (where FindChannel returns NULL) will: 1. Check LMDB cache for registered channel metadata 2. Query X3 if not in cache (which returns NOTARGET for unregistered) 3. IRCd sends FAIL TARGET_INVALID when NOTARGET received Also added null check before calling metadata_set_channel() since target_channel may be NULL for non-existent channels. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
MetadataRequest.target was using ACCOUNTLEN (15 chars) which is too short for channel names (up to 200 chars) and longer test targets. This caused target truncation and failed request matching when X3 returned NOTARGET responses. Changed to CHANNELLEN (200 chars) to accommodate all valid targets. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The channel structure allocated only enough space for the original channel name. This prevented RENAME from working when the new name was longer than the old name, returning "New channel name is too long". Fix by reallocating the channel structure when needed, updating all pointers: - Hash table entry - Global channel linked list (prev/next) - All Membership->channel pointers - User invite lists (cli_user->invited->value.chptr) - Pending destruct event The in-place rename path is preserved for the common case where the new name fits in the existing allocation. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Per IRCv3 spec, REDACT should be forwarded "in the same way as PRIVMSG messages." This means echo-message behavior applies: the sender should only receive their own REDACT back if they have the echo-message capability negotiated. Previously, the sender was always skipped. Now: - Send to all channel members with draft/message-redaction capability - Echo to sender only if they also have echo-message capability - Skip remote clients (MyUser check) This fixes tests that expect REDACT echo when both capabilities are negotiated.
When rename_channel() reallocates the channel structure for a longer name, the old pointer becomes invalid. Callers were continuing to use the stale pointer, causing use-after-free bugs. Changed rename_channel() signature to take struct Channel** so it can update the caller's pointer when reallocation occurs. All callers in m_rename.c updated to pass &chptr. This fixes RENAME tests that were failing because the response used the freed channel name instead of the new name.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
Comprehensive IRCv3.2+ capability implementations including ratified specs and draft extensions. This is a major feature branch with 86 commits across 88 files (~26,700 lines added).
IRCv3 Capabilities Implemented
Ratified Specifications
cap-302server-timetimetagecho-messageaccount-tagchghostinvite-notifylabeled-responselabeltagbatchmessage-tagsstandard-repliessetnameDraft Extensions
draft/chathistorydraft/message-redactiondraft/account-registrationdraft/multilinedraft/read-markerdraft/channel-renamedraft/event-playbackdraft/metadata-2draft/pre-awaydraft/extended-isupportdraft/no-implicit-namesdraft/webpushP10 Protocol Extensions
SEBTMLMDQStorage & Performance
LMDB Persistence
S2S Federation
X3 Integration
Additional Features
CMocka Unit Testing Framework
make testtarget for automated testingWebSocket Support
Private Message History
FEAT_CHATHISTORY_PM_REQUIRE_CONSENT)Operational Features
/STATS chathistory- Storage statistics/STATS metadata- Metadata queue statisticsFEAT_AWAY_THROTTLE- Rate limiting for AWAY changesFEAT_REGISTER_SERVER- Configurable registration targetConfiguration
New features in
ircd.conf:Build Requirements
liblmdb-dev)libzstd-dev)libcmocka-dev)Test Plan
Breaking Changes
None - all features are additive and backward compatible.
🤖 Generated with Claude Code