Skip to content

Architecture

ElixIRCd is built on the Erlang VM (BEAM) through Elixir. The core architectural principle is that each user connection is handled by an independent lightweight process.

  • Each TCP/WebSocket connection spawns a dedicated BEAM process
  • Processes communicate via message passing — no shared mutable state
  • If one connection crashes, it doesn’t affect others
  • The OTP supervisor tree automatically restarts failed processes

This model allows ElixIRCd to handle thousands of concurrent connections efficiently without threads or callback hell.

All runtime state is stored in Mnesia ETS tables (via the memento library):

TableContents
UsersAll connected users (registered and pre-registration)
ChannelsActive channels and their modes/topic
UserChannelsAssociation between users and channels, with per-channel user modes
ChannelBansBan masks for each channel (+b)
ChannelExceptsException masks (+e)
ChannelInvexesInvite exception masks (+I)
ChannelInvitesActive invitations
RegisteredNicksNickServ-registered nicknames
RegisteredChannelsChanServ-registered channels
SaslSessionsIn-progress SASL authentication sessions
UserAcceptsPer-user ACCEPT lists for Caller ID (+g)
UserSilencesPer-user SILENCE lists
UserMonitorsMONITOR target lists
HistoricalUsersWHOWAS history
JobsBackground job queue
MetricsServer metrics

Here is the full lifecycle of a user connection:

1. TCP/WebSocket connection accepted
2. Rate limiter checks:
- Max connections per IP
- Connection rate (token bucket)
3. User process spawned
4. Pre-registration phase:
- PASS (optional)
- CAP LS / CAP REQ (optional, blocks registration until CAP END)
- AUTHENTICATE via SASL (optional, during CAP negotiation)
- NICK
- USER
- CAP END
5. Handshake phase (when NICK + USER complete and not in CAP negotiation):
- Async: Reverse DNS lookup
- Async: Ident (RFC 1413) query (if ident_service.enabled = true)
- Password check (if server has a password)
- Apply connect modes (e.g., +x if cloak_on_connect = true)
- Mark user as registered
6. Welcome sequence:
- 001 RPL_WELCOME
- 002 RPL_YOURHOST
- 003 RPL_CREATED
- 004 RPL_MYINFO
- LUSERS stats
- ISUPPORT (005 tokens)
- MOTD
- Current user modes (if any)
- Notify MONITOR watchers
7. Normal operation (commands, messages)
8. Disconnection (QUIT, KILL, inactivity timeout, or error)

ElixIRCd uses two different underlying servers for its listeners:

  • ThousandIsland — for TCP (:tcp) and TLS (:tls) connections
  • Bandit — for HTTP WebSocket (:http) and HTTPS WebSocket (:https) connections

Both support tuning the number of acceptors and maximum connections.

All connections pass through the rate limiter before being fully accepted:

  1. Connection rate limiter — token bucket per IP address

    • Tracks connection frequency and enforces max_connections_per_ip
    • Violations are tracked; after block_threshold violations, the IP is blocked for block_ms
    • Exemptions by IP or CIDR range
  2. Message rate limiter — token bucket per user process

    • Applied to each message received from a connected user
    • Can be overridden per command via command_throttle
    • After disconnect_threshold violations, the user is disconnected
    • Exemptions by identified nickname, host mask, or user mode

ElixIRCd runs periodic background jobs:

JobPurpose
RegisteredNickExpirationRemoves registered nicks inactive for nick_expire_days
RegisteredChannelExpirationRemoves registered channels inactive for channel_expire_days
UnverifiedNickExpirationRemoves unverified registrations after unverified_expire_days
SaslSessionExpirationCleans up incomplete SASL sessions
ReservedNickCleanupReleases reserved nicknames (from REGAIN/RECOVER)
VerificationEmailDeliveryDelivers email verification messages

Server operators with the +s user mode receive server notices. These include events like:

  • Client connections: Client connecting: nick!ident@host (secure)
  • Other server-level events

The +s mode is restricted to IRC operators and configures the server notice mask (snomask).

All outgoing IRC messages go through the central Dispatcher module, which handles:

  • Broadcasting to individual users
  • Broadcasting to a list of users
  • Broadcasting as the server (using the server hostname as origin)
  • Broadcasting as a user (using the user’s nick!ident@host as origin)