Implement IRC user authentication (nick pinning + services verification)
Currently muaddib's IRC "trust" check (matchIrcAllowlist in src/rooms/message.ts plus the allowlist logic in src/rooms/irc/monitor.ts) only does a single thing: glob-matches the message's hostmask against patterns in userAllowlist. That is one half of authentication and leaves two real attack vectors open:
-
Nick impersonation. The current matcher accepts any nick on a host that matches a glob pattern in the allowlist. The bot doesn't verify that the nick actually belongs to the allowlisted user. If a trusted glob is *!*@*.openssl.it, any nick on a similarly-cloaked host passes. This was the diostronzo.org spoof incident the referenced bot's HOST= field was added to defend against.
-
Unregistered nick squatting. Even when the nick matches a known user, there is no check that the nick is currently registered and identified to NickServ. Someone who briefly grabs a dropped nick of an absent user (e.g. their services lapse, their nick expires) can drive the bot until either the real user comes back or an op notices.
Proposal
Adopt the three-part trust model used by https://github.com/vjt/claude-ircbot (bot.py). The relevant parts of that file are roughly:
load_trust() / is_trust_listed(nick) / host_matches(nick, host) — trust file is <nick> <host_glob> per line; both nick AND host must match the same line.
trust_check(nick, host) returns (trusted, reason) and fires a one-shot WHOIS on first sighting of a trust-listed nick.
- 307 (
RPL_WHOISREGNICK, Bahamut/Azzurra) and 330 (RPL_WHOISACCOUNT, Libera/solanum) handler adds the nick to a verified set. 318 (RPL_ENDOFWHOIS) without a prior 307/330 means unregistered.
trust_reset(nick, reason) clears the cache on PART / QUIT / NICK change.
All three checks are required: nick listed, host matches the paired glob for that nick, and the user is currently verified-registered. Anything less should arrive at the agent tagged UNTRUSTED (or dropped at the transport, depending on context — see the second item below).
Open questions for the maintainer
-
Where does the trust gate sit? In vjt's bot the trust decision lives in bot.py and is exposed to the agent as a tag on the event. In muaddib the equivalent would probably be an enriched RoomMessage (or a new trusted / auth_state field) computed in src/rooms/irc/monitor.ts before matchIrcAllowlist runs. Worth discussing whether to:
- keep the current allowlist behaviour as a separate knob (for operators who don't have access to services on their network),
- and/or make the WHOIS round-trip optional (a network like a private testnet may not issue 307/330).
-
Drop unlisted DMs at the transport? vjt's bot hard-blocks private messages from unlisted nicks at the IRC layer (dm_blocked) so the payload never reaches the agent. muaddib currently treats DMs as just another channel. Worth considering whether to add a similar gate as an opt-in (irc.dropUntrustedDMs?) — the policy alone is not enough, because by the time the LLM can apply it the payload is already in its context.
-
WHOIS flooding. Firing a WHOIS on every first sighting of a trust-listed nick could be abused if the allowlist grows. Caching with PART/QUIT/NICK-change invalidation (exactly vjt's model) should be enough, but consider rate-limiting the initial burst at connect (vjt fires one WHOIS per entry on 001).
Reference
Filed by the AI assistant strk-ai-agent on behalf of strk.
Implement IRC user authentication (nick pinning + services verification)
Currently muaddib's IRC "trust" check (
matchIrcAllowlistinsrc/rooms/message.tsplus the allowlist logic insrc/rooms/irc/monitor.ts) only does a single thing: glob-matches the message'shostmaskagainst patterns inuserAllowlist. That is one half of authentication and leaves two real attack vectors open:Nick impersonation. The current matcher accepts any nick on a host that matches a glob pattern in the allowlist. The bot doesn't verify that the nick actually belongs to the allowlisted user. If a trusted glob is
*!*@*.openssl.it, any nick on a similarly-cloaked host passes. This was the diostronzo.org spoof incident the referenced bot'sHOST=field was added to defend against.Unregistered nick squatting. Even when the nick matches a known user, there is no check that the nick is currently registered and identified to NickServ. Someone who briefly grabs a dropped nick of an absent user (e.g. their services lapse, their nick expires) can drive the bot until either the real user comes back or an op notices.
Proposal
Adopt the three-part trust model used by https://github.com/vjt/claude-ircbot (
bot.py). The relevant parts of that file are roughly:load_trust()/is_trust_listed(nick)/host_matches(nick, host)— trust file is<nick> <host_glob>per line; both nick AND host must match the same line.trust_check(nick, host)returns(trusted, reason)and fires a one-shotWHOISon first sighting of a trust-listed nick.RPL_WHOISREGNICK, Bahamut/Azzurra) and 330 (RPL_WHOISACCOUNT, Libera/solanum) handler adds the nick to averifiedset. 318 (RPL_ENDOFWHOIS) without a prior 307/330 means unregistered.trust_reset(nick, reason)clears the cache onPART/QUIT/NICKchange.All three checks are required: nick listed, host matches the paired glob for that nick, and the user is currently verified-registered. Anything less should arrive at the agent tagged
UNTRUSTED(or dropped at the transport, depending on context — see the second item below).Open questions for the maintainer
Where does the trust gate sit? In vjt's bot the trust decision lives in
bot.pyand is exposed to the agent as a tag on the event. In muaddib the equivalent would probably be an enrichedRoomMessage(or a newtrusted/auth_statefield) computed insrc/rooms/irc/monitor.tsbeforematchIrcAllowlistruns. Worth discussing whether to:Drop unlisted DMs at the transport? vjt's bot hard-blocks private messages from unlisted nicks at the IRC layer (
dm_blocked) so the payload never reaches the agent. muaddib currently treats DMs as just another channel. Worth considering whether to add a similar gate as an opt-in (irc.dropUntrustedDMs?) — the policy alone is not enough, because by the time the LLM can apply it the payload is already in its context.WHOIS flooding. Firing a WHOIS on every first sighting of a trust-listed nick could be abused if the allowlist grows. Caching with
PART/QUIT/NICK-change invalidation (exactly vjt's model) should be enough, but consider rate-limiting the initial burst at connect (vjt fires one WHOIS per entry on 001).Reference
bot.pyin particular:load_trust,is_trust_listed,host_matches,trust_check, the 307/330/318 handlers inhandle_server_line, andtrust_reset.Filed by the AI assistant strk-ai-agent on behalf of strk.