chanserv: mark registered channels +R on the wire; split z (persist) from R (registered) - #60
Draft
MrLenin wants to merge 4 commits into
Draft
chanserv: mark registered channels +R on the wire; split z (persist) from R (registered)#60MrLenin wants to merge 4 commits into
MrLenin wants to merge 4 commits into
Conversation
…RED z->R) X3's MODE_REGISTERED bit (hash.h, comment "Bahamut +r") was wired to the channel-mode letter 'z' in the P10 parser/formatter. Two problems made this dead weight: - On the nefarious fork, '+z' means the persist exmode -- an unrelated server-settable mode. X3 was misreading incoming '+z' from the ircd as a MODE_REGISTERED toggle, and would happily set/clear its own registration bit off a mode letter the fork uses for something else. - Announcing MODE_REGISTERED at all was gated behind `off_channel > 0` in every call site that set it (register, unregister, move, DB-load), and the deployed conf runs off_channel=no. So X3 never actually put +R (or, previously, +z) on the wire in the first place. Both nefarious and nefarious-upstream's m_rename only ask services for permission to rename a channel when it carries MODE_REGISTERED (channel.c:2402/2113 -- server-settable only, MODE_PARSE_FORCE required). With X3 never setting the bit, the whole services-arbitration path for channel rename was unreachable. Fix, at the root: - Remap the wire letter from 'z' to 'R' everywhere MODE_REGISTERED is parsed or emitted (mod_chanmode_parse, mod_chanmode_announce, mod_chanmode_format, clear_chanmode/CLEARMODE, the cmd_burst registration-correction path). 'z' is no longer a recognized channel mode letter in X3 at all -- it now falls through to the same "unrecognized letter" handling every other unknown letter already gets (silently ignored from server origin, rejected from user-typed strings), so an incoming fork +z persist exmode is simply left alone instead of being misread. - Announce +R/-R unconditionally on registration, unregistration, and channel move, instead of gating it behind off_channel (which governs only whether ChanServ itself occupies the channel, not whether the ircd should know the channel is registered). Also unconditional in the saxdb channel-load path for channels with a stored modelock. - Self-heal in handle_join(): if a registered, non-suspended channel's ircd-side modes don't carry +R, re-announce it. Covers ircd restarts, X3 restarts racing the first post-restart JOIN, and channel re-creation. Not gated on burst state (unlike the nearby dynamic-limit and automode/greeting guards) since it sends no message and touches no timer -- mod_chanmode_announce applies the change to local state immediately, so only the first joiner per convergence gap triggers the wire MODE. MCP_REGISTERED's guard semantics (block MODE_REGISTERED toggles from the two user-facing ChanServ mode-string paths, cmd_mode and the SET MODES modelock) are unchanged -- only the letter moved. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit 719593e)
wipeout_channel() (called from AddChannel() whenever an incoming BURST's channel timestamp predates X3's own -- the normal case on X3 restart, since chanserv_channel_read() creates placeholder chanNodes at timestamp=now before the real BURST arrives) reparsed the ircd's burst mode string via mod_chanmode(NULL, cNode, modes, modec, 0), missing MCP_FROM_SERVER. It was the only call site parsing server-origin mode data without that flag; AddChannel()'s own two mod_chanmode() calls already pass it. Without MCP_FROM_SERVER, mod_chanmode_parse()'s default case aborts the whole parse on the first mode letter it doesn't recognize, leaving cNode->modes stranded at the zero wipeout_channel just set -- losing every mode, not just +R. This fires on the ordinary X3-restart path, and in particular for any channel still carrying a stray +z on the live ircd side (the fork's persist exmode, previously planted there by X3's old z-as-registered announce before 129a0d8) -- exactly the population the +R remap needed to heal. Fix: pass MCP_FROM_SERVER, the semantically correct flag for burst data, so an unrecognized letter is skipped instead of aborting the parse. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit 01a4c4f)
Walk back the letter half of 129a0d8: X3 MUST keep emitting 'z' as the registered-channel mode letter for now (user decision 2026-08-01; see the testnet registered-mode z->R transition plan). The announce-gating half of that commit (off_channel un-gating, handle_join self-heal) stays -- it was the load-bearing fix; with off_channel=no, X3 had never emitted ANY registered marker, which left every MODE_REGISTERED-gated fork feature (rename arbitration, chathistory store-registered, metadata persistence) silently dead. - Emit conservatively: all four mod_chanmode format sites emit 'z'. - Parse liberally: both 'z' and 'R' read as MODE_REGISTERED (the fork bursts +R alongside +z once its persist-mirror sets the bit). - CLEARMODE parse accepts both; the cmd_burst unregistered-correction strips -zR. On nefarious, 'z' is the persist exmode (services-settable only), so X3 marking registered channels +z doubles as the empty-channel keep-alive those channels want -- the semantics off_channel>0 networks always had. The ircd-side companion (FEAT_REGISTERED_FROM_PERSIST mirror) makes the fork derive MODE_REGISTERED from services 'z'. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit d9aa287)
Supersedes the previous letter dance (z->R remap in 129a0d8, then the z-primary walk-back): final decision keeps BOTH letters with their true meanings, orthogonal from day one: - 'R' = MODE_REGISTERED, the ircd's registration marker. X3 emits it unconditionally on register/unregister/move/DB-load (the off_channel un-gating and handle_join self-heal from 129a0d8 stand). - 'z' = new MODE_PERSIST, nefarious's persist exmode, used as originally intended: set alongside +R only when off_channel>0 -- exactly the case where no ChanServ presence holds the registered channel open while empty. Cleared (with R) on unregistration so the emptied channel can destruct; moved on cmd_move; healed by handle_join. Parse side: 'z' now reads as MODE_PERSIST (never MODE_REGISTERED), with the same MCP_REGISTERED guard as 'R' so user-typed modelocks can't toggle server-managed markers. CLEARMODE accepts both letters. The cmd_burst correction for channels we don't recognize strips both (-zR). No ircd-side companion needed: the fork's MODE_REGISTERED machinery (rename arbitration, chathistory store-registered, metadata persistence) keys off the R that X3 now actually emits. The FEAT_REGISTERED_FROM_PERSIST mirror has been reverted from both ircd trees. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit 1d80d7d)
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
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.
Split out of #57 (which is now the channel-rename half only). Fixes X3's registered-channel wire marker so nefarious2's registered-channel logic can actually trigger.
What this does
+Ron the wire. X3'sMODE_REGISTEREDletter was the Bahamut-eraz— which nefarious2 doesn't use for registered (on the forkzis the persist exmode). Remapped toR, nefarious2's actual server-settable registered mode. Announced unconditionally at register / unregister / move / db-load — previously all gated onoff_channel > 0, so with a bot in the channel the ircd was never told the channel was registered. Without this, no ircd-side registered-channel logic (rename arbitration, registered-only history retention, etc.) could ever fire.handle_joinre-asserts the marker on a registered channel the ircd doesn't currently show as+R(ircd restarts, X3 restarts racing the first post-restart JOIN, channel re-creation). Only the first joiner triggers a wire MODE; later joins in the same burst see the bit set and no-op.z(persist) split fromR(registered) properly — newMODE_PERSISTbit tracks+z. ChanServ sets+zalongside+Rexactly whenoff_channel > 0(no bot presence to hold the channel open —+z's original purpose), clears both on unregister so an unregistered channel can die when it empties.cmd_burststrips both markers from a channel that isn't registered with us. User-typed mode strings (MCP_REGISTEREDcall sites: modelock / mode command) may not toggle either server-managed marker; server-origin strings parse both so X3's copy tracks the ircd.wipeout_channelparses burst modes as server-origin (MCP_FROM_SERVER). Previously one unknown mode letter aborted the whole parse, stranding a channel's mode mirror at zero on every X3 restart.Commit history note
The three z/R commits show the design being worked out in-branch (
+R→ "keepzon the wire for now" → proper split). Happy to squash them to one if preferred; kept as-is so the reasoning in each message survives.Related
channel.cMODE_REGISTERED.Verification
Live E2E on the Afternet testnet against the fork:
+Rvisible on registered channels at register / db-load / rejoin after ircd restart;-Ron unregister;+zonly whenoff_channel > 0. Registered-channel-gated ircd behaviour (rename arbitration in #57) exercised end-to-end. Vitest coverage in the testnet repo (channel-rename-services.test.ts,persistence-profile.test.ts).🤖 Generated with Claude Code