IRC Client Foundations Supplement

Version: 1.0 Status: Ready for implementation Companion to: docs/pdf.md (feature PRD) Purpose: Build-from-scratch decisions for the embeddable rabbot-irc gem


How to use this document

The main PRD (pdf.md) covers mIRC/Ibis feature parity, phased deliverables, services caveats, and the Fandom companion bot. This supplement covers everything needed before rails new or gem scaffolding: packaging, auth, schema, workers, embed API, formatter spec, dialog triage, deployment, and tests.

Decisions locked in for v1:


14. Gem packaging and embed API

14.1 Gem name and repos

Package Role
rabbot-core (extract from rabbot) IrcFormat, IrcReply, Normalize, CharacterProfile, CharacterProfileGenerator, CharacterTranscripts
rabbot-irc (new repo) Rails engine, workers, event store, Hotwire UI
rabbot (existing) Cinch bots, plugins, companion bot provisioning

rabbot-irc depends on rabbot-core and ircinch. Host apps add gem "rabbot-irc" and mount or embed.

14.2 Engine structure

rabbot-irc/
  lib/rabbot/irc/
    engine.rb                    # Rails::Engine, isolated namespace Rabbot::Irc
    version.rb
  app/channels/irc_channel.rb
  app/controllers/rabbot/irc/
  app/javascript/controllers/irc_chat_controller.js
  app/views/rabbot/irc/chat/     # partials: log, nicklist, input, status
  app/services/rabbot/irc/
  config/routes.rb
  lib/generators/rabbot/irc/install/

14.3 Host app integration

Full-page mount (optional):

# config/routes.rb
mount Rabbot::Irc::Engine => "/irc"

Embed in any view:

<div id="sidebar"
     data-controller="irc-chat"
     data-irc-chat-layout-value="compact"
     data-irc-chat-session-url-value="<%= rabbot_irc_session_path %>">
</div>

Generator:

rails generate rabbot:irc:install
rails db:migrate

Copies migrations for irc_* tables; adds importmap/Stimulus pin for irc_chat_controller.

14.4 Embed contract

Concern Rule
Mount target Stimulus irc-chat scopes all UI under the host element
Layout modes full, compact, log-only β€” CSS + partial selection via data-irc-chat-layout-value
ActionCable Subscribe with signed_session_id from host session
Body ownership Engine never assumes full-page layout; works in sidebar, modal, or iframe-like div
Theming Host app CSS variables (--irc-bg, --irc-nick-op, etc.) optional override

14.5 Architecture diagram

HostApp (any Rails app)
  β”œβ”€β”€ mount Rabbot::Irc::Engine => "/irc"     [optional full page]
  └── embed <div data-controller="irc-chat">  [any DOM element]
           β”‚
           β–Ό
      irc_chat_controller (Stimulus)
           β”‚
           β–Ό
      ActionCable (IrcChannel)
           β”‚
           β–Ό
      IrcWorker process (bin/irc_worker)
           β”‚
           β–Ό
      Cinch (ircinch) ↔ IRC server

15. Auth and sessions

15.1 Two tiers

Tier Connect Persistence Companion bot Saved networks
Anonymous IRC connect form Session cookie only; lost on expiry No No
Account Optional login first DB profiles, event cursors, replay Yes (main PRD Β§13) Yes

Anonymous users get live chat while the tab is open. Creating an account (or signing in) unlocks saved network profiles, IrcEventStore replay, and companion bot settings.

15.2 Session model

15.3 Anonymous limits

15.4 Account unlocks


16. Data model

Host app runs engine migrations via rails rabbot:irc:install:migrations.

16.1 Tables

Table Purpose
users Host app (optional until account features)
irc_sessions Browser session ↔ worker; signed_token, user_id (nullable), state
irc_network_profiles Per-user saved server, port, nick, SSL, profile name
irc_credentials Encrypted password / SASL secret (has_encrypted per profile)
irc_connections Worker-side Cinch connection state, session_id, last_ping_at
irc_events Append-only log for replay (account + companion)
irc_companion_profiles Fandom URL, visibility, enabled flag (Β§13)
irc_channel_states Latest topic, modes, nick list snapshot per channel

16.2 irc_events schema (key fields)

Column Type Notes
user_id bigint Owner (required for replay)
network string Normalized network key
channel string chan or query nick
event_type string PRIVMSG, JOIN, PART, etc.
payload jsonb Raw parsed fields
formatted_text text Output of event_formatter
monotonic_id bigint Per-user sequence for cursor
recorded_at datetime IRC event time

Index: (user_id, monotonic_id), (user_id, network, channel, monotonic_id).

16.3 Retention

Defaults (configurable via Rabbot::Irc.configure):

16.4 Database

Web platform (rabbot-irc):

Legacy Rabbot personas:

Shared boundary (Postgres only):


17. IRC worker architecture

17.1 Library

Use Cinch (ircinch) β€” same as existing Rabbot. Do not use Net::IRC or EventMachine for the web client worker.

17.2 Process model

Process Command Role
Web bin/rails server HTTP, Turbo, ActionCable
IRC worker bin/irc_worker Long-lived; holds Cinch sockets
Jobs bin/jobs (Solid Queue) Companion lifecycle, compaction, profile generation

IrcReadJob is for short tasks only (parse MOTD, probe capabilities) β€” not holding IRC sockets inside Solid Queue workers.

17.3 Worker loop

  1. IrcWorker polls irc_connections for pending / stop commands (DB row or Redis list)

  2. Spawns Cinch bot per active irc_connection

  3. On IRC event β†’ format β†’ broadcast ActionCable + optionally append irc_events

  4. On disconnect β†’ update state; apply reconnect policy from IrcReconnectGuard patterns (lib/irc_reconnect_guard.rb): throttle floor, drone floor, connect stagger

17.4 Connection lifecycle by phase

Phase Default behaviour
Phase 1 One Cinch connection per IrcSession while tab subscribed; disconnect when last tab leaves
Phase 2 + account Saved profiles; optional stay-connected via companion (invisible mode)
Phase 3 Visible companion = second Cinch connection mirroring channel set

17.5 Duplicate event rule

When the user’s web session is online (active Cinch connection), log events from that connection only. Companion bot appends to irc_events only when the user connection is absent (tab closed, invisible bouncer mode). Prevents duplicate lines on reconnect.


18. Routes, UI map, and Turbo structure

Engine routes (under mount prefix, e.g. /irc):

Method Path Purpose
GET / Full-page chat shell (optional)
POST /connect Start IrcSession, queue worker
DELETE /disconnect End session
POST /channels JOIN
DELETE /channels/:name PART
POST /messages PRIVMSG to channel or query
GET /sync Catch-up since since=<monotonic_id>
GET /settings Turbo Frame modal
GET /health Worker heartbeat (Β§21)

18.1 Turbo layout (inside mount element)

[data-controller="irc-chat"]
  turbo-frame#irc-channel-list
  turbo-frame#irc-main
    turbo-frame#irc-log
    turbo-frame#irc-nicklist
  turbo-frame#irc-status
  form#irc-input β†’ POST /messages

18.2 Input routing

Input Handler
/join #chan Forward to worker as JOIN
/part, /msg nick text, /nick new IRC command dispatcher
Plain text PRIVMSG to active channel or query
Context menu actions Turbo POST; not raw IRC

19. style.ibis formatter spec (Phase 1)

Map reference/stuff/style.ibis to Rabbot::Irc::EventFormatter. Colour variables from vars.ibis / identifiers1.ibis become IrcClientConfig defaults (mIRC indices 0–98).

19.1 Event templates

Event style.ibis Method Output pattern
JOIN L56–66 format_join Coloured nick + Joins #chan (host) (ppl: N)
PART L68 format_part Coloured nick + Parted #chan + optional (reason)
QUIT L98–106 format_quit Address + reason; netsplit when $1- matches split pattern
KICK L77 format_kick knick been kicked from #chan (reason) (ppl: N)
TOPIC L78 format_topic Coloured nick + New Topic Set: + topic (plain body)
PRIVMSG # L52 format_channel_msg Timestamp + coloured nick + plain message body
PRIVMSG ? L53 format_query_msg Indented PM: nick bar + plain text
ACTION L54–55 format_action * nick action with action colour
NICK L89–96 format_nick_change Changed Nick (Previously known as old)
NOTICE L79–86 format_notice Route to active window, comchan, or status log

19.2 Web-specific mapping

Ibis concept Web implementation
%echo_indent CSS padding-left on log rows (configurable px)
$its timestamp <time datetime="..."> element
colorNick Nick colour from hash or @ list; op/voice prefix in nick list
Plain body rule Match lib/CONVENTIONS.md β€” colour on nicks/events, not message body

19.3 Golden tests

Add test/fixtures/irc/style_ibis_events.yml with input IRC hashes + expected HTML/mIRC formatted output. Reuse existing IrcFormat test patterns from rabbot-core.


20. Dialog priority matrix

Legacy scan found 20 dialogs; most are not worth porting.

Dialog Phase Action
settings 2 Implement β€” auto-join, colours, echo indent, companion bot
setup_autojoin 2 Merge into settings
topic 2 Channel topic editor (Turbo Frame)
whois / who 2 Context menu β†’ Turbo partial
startup 2 Connection notes panel (Β§12.3 in main PRD)
setup_flood 3 Flood limits
lock 3 Screen lock / away overlay
setup_ban 3 Ban list viewer
xdccleech, mp3, pc, asdf, draw, diuqil, ircspy, first, chan, tdccchangesettings, whoischan β€” Drop v1 β€” document as non-goals

21. Deployment and environment

21.1 Processes

Process Role
web Puma + ActionCable
irc_worker Cinch connections (scale horizontally with connection sharding)
solid_queue Companion lifecycle, event compaction, fandom profile generation

21.2 Environment variables

RABBOT_IRC_WORKER_COUNT=2
RABBOT_IRC_EVENT_RETENTION_DAYS=7
RABBOT_IRC_EVENT_RETENTION_MAX=50000
RABBOT_IRC_ACTION_CABLE_ADAPTER=redis
RABBOT_IRC_COMPANION_ENABLED=true
RABBOT_THROTTLE_RECONNECT_FLOOR=6
RABBOT_DRONE_RECONNECT_FLOOR=8
RABBOT_MESH_HOST=127.0.0.1

21.3 Secrets

21.4 Health check

GET /irc/health returns JSON:

{ "workers_alive": 2, "active_connections": 14, "oldest_ping_sec": 3 }

22. Test plan

Layer Tool Scope
Unit Minitest EventFormatter, CapabilityProbe, SyncCatchup, CharacterTranscripts.fandom_url?
Integration Mock IRC server (e.g. Celluloid IRC test double or local ircd) Cinch connect, JOIN, PRIVMSG, reconnect
System Capybara + ActionCable::Connection::TestCase Embed in <div>, send message, assert Turbo append
Golden YAML fixtures style.ibis events vs formatted output
Staging smoke Manual on Undernet trout Not in default CI

22.1 Companion bot tests

22.2 Embed tests


23. Scale and data plane (later phases)

Not required for MVP. Documented for growth toward large user counts.

23.1 Data stores

Layer Technology Role
Authoritative PostgreSQL Users, sessions, profiles, hot irc_events (partitioned)
Realtime Redis ActionCable/pubsub, presence, rate limits, worker queue, hot log buffer
Ingest (at scale) Redis Streams or Kafka Event pipe between IRC workers and WebSocket gateways
Cold tier Object storage (S3) Aged events beyond retention window
HTTP edge Vinyl Cache Static assets and cacheable GETs only β€” not IRC/WebSocket

23.2 Application boundaries

23.3 Deferred infrastructure


24. Federated capacity (mesh donate)

Optional community capacity β€” extends the spirit of DirectchatMesh, with a new control plane.

24.1 Model

24.2 Non-goals


End of foundations supplement. Feature scope remains in pdf.md. Script inventory: ibis-script-reference.md.