P11 slice 294 — OKICK (operator force-kick bypassing chanop status)#
What & why#
charybdis extensions/m_okick.c: an operator force-kick. OKICK <channel> <user> [:comment]
forcibly removes user from channel without requiring the oper to be a chanop — or even a
member of the channel, sourced from the server so the channel sees :<server> KICK <chan> <user> :<comment>. It is the oper-override sibling of KICK (chanop-gated, slice baseline) and
REMOVE (chanop force-part, slice 290), and the kick-shaped member of the charybdis operator
channel-intervention family (SAJOIN/SAPART/SANICK/SAMODE/OJOIN/OPME/REMOVE).
Distinct from everything that exists: KICK/REMOVE enforce chanop + rank protection; the SA*
family forces part/join/nick/mode. There is no oper-override visible-KICK yet. OKICK fills it.
Gate order (command/okick.rs)#
- Not an IRC operator (
+o/+O) →481 ERR_NOPRIVILEGES(OPME/SAJOIN precedent: plain oper bit, not a per-commandOperPrivilege— charybdism_okickis plainmg_not_opertoo). - Missing channel or user param →
461 ERR_NEEDMOREPARAMS("OKICK"). - Unknown victim nick →
401 ERR_NOSUCHNICK. - Channel does not exist →
403 ERR_NOSUCHCHANNEL. - Victim not on the channel →
441 ERR_USERNOTINCHANNEL. - Victim holds
+Y(official-join) →482 ERR_CHANOPRIVSNEEDED"official network business" (divergence from charybdis's raw removal: leveva's+Yimmunity is documented absolute, even an oper override respects it — keeps the [[leveva-ojoin-official-join]] invariant true). - Success → remove victim, broadcast
:<server> KICK <chan> <victim> :<comment>to every local member (incl. the victim), echo the KICK back to the oper (always — the oper need not be a member; excluded from the audience fan to avoid a duplicate), relay server-sourced S2S, and post a+saudit snomask.
Comment defaults to the oper's nick (KICK precedent) when absent.
Channel seam — Channels::okick (the fuzz seam)#
New atomic seam, single lock, no permission/rank check (that is the point):
pub enum OkickOutcome {
NoSuchChannel,
NotInChannel { display: String },
OfficialBusiness { display: String },
Kicked { display: String, audience: Vec<Uid> },
}
pub fn okick(&self, name: &str, victim: &Uid) -> OkickOutcome
- channel missing →
NoSuchChannel - victim not a member →
NotInChannel { display } - victim
+Y→OfficialBusiness { display } - else remove victim, return
Kicked { display, audience(pre-removal roster) }, delete the channel if now empty (destroy_when_empty).
S2S — s2s::relay::server_kick#
New helper mirroring server_channel_mode (server-sourced): broadcast :<our_sid> KICK <chan> <victim_uid> :<reason>. Inbound inbound_kick already accepts a SID prefix via source_mask
(renders the server name), so the path is symmetric — no inbound change needed.
Divergences (documented)#
- leveva-native — IRCnet 2.11 has no OKICK (charybdis extension); no oracle, no differential.
- Plain oper-bit gate — any oper, not a per-command
OperPrivilege(OPME/OLIST/TESTLINE precedent); charybdis OKICK has no distinct privilege either. +Yimmunity respected — charybdis OKICK raw-removes (bypasses everything); leveva keeps the+Yofficial-join absolute-immunity invariant, refusing with482. Documented, deliberate.- Oper always gets the KICK echo — even when not a member (every leveva handler confirms to the actor); charybdis sends the oper nothing extra when they are not on the channel.
+saudit snomask not+wwallops — SA*/OJOIN/OPME family precedent (slices 184/186/292); charybdis sends a wallops. The audit wording is leveva's.- Local-only — removal of a local-or-remote member of this server's channel; the S2S relay carries the removal onward, but OKICK itself is a per-server command (charybdis is per-server).
Tests (TDD; inverse invariants + fuzzing)#
channel.rsunits:okick_removes_any_member_without_a_permission_check(bypasses chanop/rank — a plain member is removed, the roster shrinks, channel survives; inverse — re-join is clean);okick_refuses_an_official_join_member(+Y→OfficialBusiness, member stays); the missing-channel/missing-member arms; last-member removal deletes the channel.command/okick.rsunits:481non-oper (no removal);461missing args;401unknown nick;403ghost channel;441victim-not-on-channel;482+Yvictim; success (oper not a member) → server-sourced KICK echoed to oper + co-member sees it + victim gone; default comment = oper nick; inverse — the gated/refused paths change nothing.tests/okick_proptest.rs(2 props):okick_outcome_matches_channel_state(random member/op/+Y- ghost target → exact outcome; victim present afterwards iff not removed) +
arbitrary_args_never_panic(replies ⊆{481, 461, 401, 403, 441, 482, KICK}).
- ghost target → exact outcome; victim present afterwards iff not removed) +
tests/golden_okick.rs(real binary,dline.kdloper): bob owns#rust(carol a plain member); alice opers but never joins; non-oper bobOKICK→481; aliceOKICK #rust carol :spam→ server-sourced KICK seen by bob/carol + echoed to alice; carol gone;OKICK #ghost x→403.
Wiring#
mod okick;+"OKICK" => okick::okick(...)incommand/mod.rs.help/OKICK.md+OKICKinhelp.rsCOMMANDS allowlist + the operator line inhelp/index.md.
Gate#
cargo test -p leveva green; cargo clippy -p leveva --tests clean; cargo build --workspace 0
warnings.