Exploring the roots and future of Internet Relay Chat

The IRC Protocol Explained: From PRIVMSG to CTCP

You can learn IRC's protocol in an afternoon. I'm serious. It's one of the simplest application-layer protocols in common use. The entire thing is human-readable text — no binary framing, no compression, no protocol buffers. You can connect to an IRC server with telnet and type raw commands. Try that with HTTP/2.

Understanding the protocol isn't just academic. If you write bots, debug connection issues, or run a server, knowing what's happening at the wire level saves hours of frustration. Let's walk through it.

Why Understand the Protocol?

Most IRC users never see the raw protocol. Their client handles everything — connection, registration, channel joins, message formatting. But if you're doing anything beyond basic chatting, protocol knowledge becomes essential:

  • Writing a bot? You need to understand message parsing and the command set.
  • Debugging connection issues? Knowing the registration sequence tells you where things are failing.
  • Running a server? Understanding mode strings, numeric replies, and server-to-server protocol helps you diagnose problems.
  • Contributing to an IRC client or server? The protocol IS the specification.

The Basics: Text Over TCP

IRC operates over TCP, typically on port 6667 (plaintext) or 6697 (TLS). The protocol is entirely text-based — both client-to-server and server-to-client messages are UTF-8 (or historically, Latin-1) text, terminated by CR-LF (\r\n).

Each message is a single line, maximum 512 bytes (including the CR-LF terminator). This 512-byte limit is one of IRC's most well-known constraints — it means long messages get truncated, which is why you sometimes see messages cut off mid-sentence. The IRCv3 message-tags extension relaxes this to 8191 bytes for the tags portion, but the main message body remains limited.

Here's what a raw IRC session looks like when you connect with netcat:

$ nc irc.libera.chat 6667
:irc.libera.chat NOTICE * :*** Checking Ident
:irc.libera.chat NOTICE * :*** Looking up your hostname...
:irc.libera.chat NOTICE * :*** Couldn't look up your hostname
NICK testuser
USER testuser 0 * :Test User
:irc.libera.chat 001 testuser :Welcome to the Libera.Chat Internet Relay Chat Network testuser
:irc.libera.chat 002 testuser :Your host is irc.libera.chat, running version solanum-1.0
:irc.libera.chat 003 testuser :This server was created ...
...

That's it. That's the protocol. Text in, text out.

Message Format

Every IRC message follows the same structure, defined in RFC 1459 and refined in RFC 2812:

[@tags] [:prefix] COMMAND [params] [:trailing]

Breaking this down:

  • Tags (optional, IRCv3): Key-value pairs prefixed with @, used for metadata like server timestamps, account names, and message IDs.
  • Prefix (optional): Identifies the source of the message. For messages from servers, it's the server name. For messages from users, it's nick!user@host.
  • Command: Either a named command (PRIVMSG, JOIN, NICK) or a three-digit numeric code (001, 433, 353).
  • Params: Space-separated parameters. The last parameter can contain spaces if it's preceded by a colon (the "trailing" parameter).

Examples:

# Server welcome message
:irc.libera.chat 001 marco :Welcome to the Libera.Chat IRC Network marco

# Channel message from a user
:alice!alice@user/alice PRIVMSG #linux :Has anyone tried the new kernel release?

# User joining a channel
:bob!bob@gateway/web JOIN #python

# Mode change
:ChanServ!ChanServ@services. MODE #help +o marco

# With IRCv3 tags
@time=2026-01-15T14:30:00.000Z;account=alice :alice!alice@user/alice PRIVMSG #linux :Testing message tags

Connection Sequence

When a client connects to an IRC server, the registration sequence is straightforward:

# Client sends (in this order):
CAP LS 302                           # IRCv3: request capability list
PASS serverpassword                   # Optional: server password
NICK marco                           # Set nickname
USER marco 0 * :Marco Pellegrini     # Set username and realname

# If using SASL (IRCv3):
CAP REQ :sasl                        # Request SASL capability
AUTHENTICATE PLAIN                    # Start SASL PLAIN auth
AUTHENTICATE bWFyY28AbWFyY28AcGFzcw== # Base64 encoded credentials
CAP END                              # End capability negotiation

# Server responds with:
:server 001 marco :Welcome ...        # RPL_WELCOME
:server 002 marco :Your host is ...   # RPL_YOURHOST
:server 003 marco :This server ...    # RPL_CREATED
:server 004 marco server version ...  # RPL_MYINFO
:server 005 marco CHANTYPES=# ...    # RPL_ISUPPORT (server capabilities)

The 005 numeric (RPL_ISUPPORT) is particularly important — it tells the client what features the server supports: maximum channel name length, supported channel types, maximum nickname length, available channel modes, and dozens of other parameters. A well-written client uses this information to adapt its behavior to each server's capabilities.

Core Commands

The IRC command set is larger than most people realize, but a handful of commands handle 95% of normal usage:

PRIVMSG — Send a message (both channel and private):

PRIVMSG #channel :Hello everyone
PRIVMSG alice :Hey, got a minute?

NOTICE — Like PRIVMSG but should not trigger automatic replies (to prevent bot loops):

NOTICE #channel :Server maintenance in 10 minutes
NOTICE alice :Your file transfer is ready

JOIN — Enter a channel:

JOIN #linux
JOIN #secret secretkey
JOIN #chan1,#chan2,#chan3

PART — Leave a channel:

PART #linux
PART #linux :Going to lunch, back in an hour

MODE — Set channel or user modes:

MODE #linux +o alice               # Give alice operator status
MODE #linux +m                     # Set channel to moderated
MODE #linux +b *!*@spammer.com     # Ban a host
MODE marco +i                      # Set yourself as invisible

KICK — Remove a user from a channel:

KICK #linux troll :Read the rules before coming back

TOPIC — Set or view channel topic:

TOPIC #linux
TOPIC #linux :Welcome to #linux | Rules: https://example.com/rules

WHO / WHOIS / WHOWAS — Query user information:

WHOIS alice
WHO #linux
WHOWAS oldfriend

PING / PONG — Keepalive mechanism:

# Server sends:
PING :irc.libera.chat
# Client must respond:
PONG :irc.libera.chat
# If client doesn't respond, server disconnects them

Numeric Replies

The server communicates status information through three-digit numeric codes. There are over 100 defined numerics, but here are the ones you'll encounter most often:

001 RPL_WELCOME        — Registration successful
002 RPL_YOURHOST       — Server identification
003 RPL_CREATED        — Server creation date
004 RPL_MYINFO         — Server name, version, available modes
005 RPL_ISUPPORT       — Server capabilities and limits
251-255                — User/channel statistics
301 RPL_AWAY           — User's away message
311 RPL_WHOISUSER      — WHOIS response: user info
332 RPL_TOPIC          — Channel topic
353 RPL_NAMREPLY       — Channel member list
366 RPL_ENDOFNAMES     — End of NAMES list
372 RPL_MOTD           — Message of the day line
376 RPL_ENDOFMOTD      — End of MOTD
401 ERR_NOSUCHNICK     — No such nick/channel
403 ERR_NOSUCHCHANNEL  — Channel doesn't exist
421 ERR_UNKNOWNCOMMAND — Unknown command
432 ERR_ERRONEUSNICK   — Invalid nickname characters
433 ERR_NICKNAMEINUSE  — Nickname already taken
461 ERR_NEEDMOREPARAMS — Not enough parameters
473 ERR_INVITEONLYCHAN — Channel is invite-only
474 ERR_BANNEDFROMCHAN — Banned from channel
475 ERR_BADCHANNELKEY  — Wrong channel key

Learning the common numerics makes debugging IRC issues dramatically easier. When your client shows a mysterious error, the numeric tells you exactly what went wrong.

CTCP: Client-to-Client Protocol

CTCP (Client-to-Client Protocol) is a layer built on top of PRIVMSG for exchanging metadata between clients. CTCP messages are wrapped in ASCII character 0x01 (SOH):

# CTCP VERSION request (sent as PRIVMSG):
PRIVMSG alice :\x01VERSION\x01

# Client responds with NOTICE:
NOTICE marco :\x01VERSION WeeChat 4.2.0\x01

# Common CTCP commands:
VERSION    — Request client software information
TIME       — Request client's local time
PING       — Measure round-trip latency
CLIENTINFO — List supported CTCP commands
ACTION     — The /me command (/me waves → CTCP ACTION)
DCC        — Direct Client-to-Client connections

The ACTION command is the most widely used CTCP message — every time someone types /me does something, it's actually a CTCP ACTION wrapped in a PRIVMSG. This is one of those protocol details that surprises people: the beloved /me is not a native IRC command but a CTCP convention.

IRCv3 Extensions

The IRCv3 working group has been extending IRC's capabilities while maintaining backward compatibility. Key extensions include:

  • message-tags: Arbitrary key-value metadata on messages (timestamps, account names, message IDs)
  • server-time: Server-provided timestamps on messages, enabling accurate history playback
  • SASL: Secure authentication mechanism, supporting PLAIN, EXTERNAL (client certificates), and SCRAM
  • account-tag: Attach the sender's account name to every message
  • away-notify: Real-time notification when users go away or return
  • cap-notify: Dynamic capability negotiation after connection
  • echo-message: Server echoes your own messages back to you, confirming delivery
  • chathistory: Server-side message history retrieval

The chathistory extension is particularly significant because it addresses one of IRC's oldest limitations — the inability to see messages sent while you were offline. With chathistory support, a client can request previous messages from the server, similar to how modern chat platforms work.

These extensions are opt-in: clients negotiate capabilities during connection, and servers only send extended features to clients that explicitly request them. This ensures backward compatibility — a 20-year-old IRC client still works on a modern server; it just doesn't get the new features.

Frequently Asked Questions

What's the maximum IRC message length?

512 bytes including the CR-LF terminator, as defined in RFC 2812. In practice, the usable message length is shorter because the prefix (nick!user@host) is included in this count when the server relays the message. The actual text you can send is typically 400-450 bytes, depending on the length of your hostmask.

Is IRC encrypted?

Modern IRC connections use TLS (port 6697 by convention). Client-to-server encryption is well-supported by all major clients and servers. Server-to-server links within a network are also typically encrypted. However, the IRC server itself can see all messages in plaintext — end-to-end encryption between users requires additional tools like OTR or OMEMO.

What's the difference between PRIVMSG and NOTICE?

Functionally, they're identical — both send a text message to a target (user or channel). The difference is in how clients should handle them: NOTICE should never trigger an automatic reply (to prevent infinite loops between bots), while PRIVMSG may trigger auto-replies. Bots should use NOTICE for responses; humans typically use PRIVMSG.

Can I write my own IRC client?

Yes, and it's an excellent programming exercise. A minimal IRC client needs only: TCP socket connection, NICK/USER registration, PING/PONG handling, and PRIVMSG sending/receiving. You can build a working client in under 200 lines of Python, Go, or any language with TCP socket support.

FAQ

How does the IRC protocol work?

IRC uses a client-server model over TCP. Clients connect to servers, which may be linked in a network. Communication uses simple text-based commands (PRIVMSG, JOIN, NICK) defined in RFC 1459 and extended by IRCv3.

What port does IRC use?

Traditional IRC uses port 6667 for plaintext and 6697 for TLS-encrypted connections. Some networks also use port 7000 or 7070 for TLS.

What is IRCv3?

IRCv3 is a working group that develops extensions to the IRC protocol, including capabilities negotiation, message tags, account tracking, and other features that modernize IRC without breaking backward compatibility.

Is IRC encrypted?

IRC supports TLS encryption between client and server. However, server-to-server links may or may not be encrypted depending on the network's configuration. End-to-end encryption is not built into the protocol but can be layered using OTR or OMEMO.