Multiline Messages

Copyright © 2019 James Wheare <>

Unlimited redistribution and modification of this document is allowed provided that the above copyright notice and this permission notice remains intact.

This specification is a work-in-progress and may have major incompatible changes without warning.

This specification may change at any time and we do not recommend implementing it in a production environment.

Notes for implementing work-in-progress version πŸ”—

This is a work-in-progress specification.

Software implementing this work-in-progress specification MUST NOT use the unprefixed multiline-concat tag or multiline CAP/batch names. Instead, implementations SHOULD use the draft/multiline-concat tag, draft/multiline CAP and draft/multiline batch names to be interoperable with other software implementing a compatible work-in-progress version.

The final version of the specification will use an unprefixed tag name.

Introduction πŸ”—

This specification adds a new batch type and tag sent by clients and servers to send messages that can exceed the usual byte length limit and that can contain line breaks.

Motivation πŸ”—

IRC messages have been traditionally limited by the line-based nature of the protocol and the line length limit of 512. To work around these limitations, client implementations split longer messages into multiple lines, and users need to rely on snippet hosting services to send multiple lines without flooding the chat or having their messages interrupted by other users.

Mulitline messages allow for a more coherent experience that avoids the disconnected messages that result from these workarounds.

Architecture πŸ”—

Dependencies πŸ”—

This specification depends on the batch capability which MUST be negotiated to use multiline messages. The order of capability negotiation is not significant and MUST not be enforced.

This specification also uses the client batch, message tags, and standard replies frameworks.

Capabilities πŸ”—

This specification adds the draft/multiline capability.

Implementations requesting this capability indicate that they are capable of handling the batch type and message tag described below.

The capability MUST be advertised by servers with a REQUIRED value: a comma (,) (0x2C) separated list of tokens. Each token consists of a key with an optional value attached. If there is a value attached, the value is separated from the key by an equals sign (=) (0x3D). That is, <key>[=<value>][,<key2>[=<value2>][,<keyN>[=<valueN>]]]. Keys specified in this document MUST only occur at most once.

Clients MUST ignore every token with a key that they don’t understand.

The only defined capability key so far is:

Tags πŸ”—

This specification adds the draft/multiline-concat message tag, which has no value and is used to send message lines that extend beyond the usual byte length limit. Its usage is described below.

Batch types πŸ”—

This specification adds the draft/multiline batch type.

In addition to the base batch parameters (reference-tag and type) a multiline batch has one additional parameter, the target recipient.

Multiline batches MUST only contain one or more PRIVMSG lines, or one or more NOTICE lines. These lines MUST all have a target which matches the batch target.

When receiving a well-formed multiline message batch, implementations MUST collect the message content from each line in the batch and wait until the full batch has been received before processing the message. Processing in this context refers to:

The combined message value of a multiline batch is defined as the concatenation of the messages from each individual line within the batch. Line messages are joined by a single line feed (\n) byte unless the draft/multiline-concat message tag is sent, in which case that line’s message is directly joined with the previous line’s message with no separation.

Each line feed used to join line messages contributes one byte towards the max-bytes limit. No line feed is appended to the final line message of a batch.

Servers MUST NOT reject blank lines other than in the following cases:

Fallback πŸ”—

When delivering multiline batches to clients that have not negotiated the multiline capability, servers MUST deliver the component messages without using a multiline BATCH.

Servers MUST NOT send blank lines to clients that have not negotiated the multiline capability.

Servers SHOULD maintain the line composition sent by the client instead of combining to a normalised form before re-splitting. This ensures that steps taken to split long lines appropriately are preserved.

Any tags that would have been added to the batch, e.g. message IDs, account tags etc MUST be included on the first message line to be sent. Tags MAY also be included on subsequent lines where it makes sense to do so.

Errors πŸ”—

Servers MUST use FAIL messages from the standard replies framework to notify clients of errors with multiline batches. The command is BATCH and the following codes are defined:

Interaction with other specifications πŸ”—

Message tags (spec) πŸ”—

Clients MUST NOT send tags other than draft/multiline-concat and batch on messages within the batch. In particular, all client-only tags associated with the message must be sent attached to the initial BATCH command.

Message ids (spec) πŸ”—

Servers MUST only include a message ID on the first message of a batch when sending a fallback to non-supporting clients.

Labeled response (spec) with echo-message (spec) πŸ”—

Servers MUST only include the label tag on the opening BATCH command when replying to a client using echo-message.

Client implementation considerations πŸ”—

This section is non-normative.

Splitting long lines πŸ”—

When using draft/multiline-concat to split longer lines, care is required to ensure messages are displayed appropriately when combined or in the fallback case.

The recommended splitting method is to split long lines between words where possible and leave the space character at the end of the line. This ensures that pairs of words spanning a line concatenation are still space separated, without adding a space to the start of any fallback lines.

Take care not to split in the middle of multi-byte character sequences.

In order to calculate the maximum allowed space before splitting a line, consider the following line:

:nick!~user@host PRIVMSG #channel :hello \r\n

The parts of a message that must fit within the 512 byte limit are as follows

part syntax length
leading : 1
nick nick variable
nick/mask separator ! 1
user ~user variable
user/host separator @ 1
host host variable
command PRIVMSG 7
channel #channel variable
message separator : 2
crlf \r\n 2
message hello variable

Not all parts may appear in a message, but a safe limit should take them into account and include an extra safety buffer in case of any unexpected syntax added by a server, e.g. a statusmsg prefix.

With a safety buffer of 10, the total message size available before a split is needed can be calculated as follows

512 - 14 - 10 - <nick> - <user> - <host> - <channel>

Clients might wish to perform a WHO query on themselves when connecting to a server, and keep track of numeric 396 RPL_VISIBLEHOST to keep their user@host mask in sync.

Clients might alternatively prefer to use a hard coded limit. A possible worst case scenario for the variable parts might be:

Which would leave 353 bytes for each message.

Server implementation considerations πŸ”—

This section is non-normative.

For server implementations and operators, consider the impact of relaying a large multiline batch. In particular, the potential to exhaust client send queue budgets, which will either result in client disconnection (a denial-of-service attack) or degraded client performance.

Each additional protocol line carries extra metadata beyond the message content, such as the sender’s full source mask, the command, and the target. This means that the byte size of a multiline batch sent to clients will typically be a function of max-lines as well as max-bytes. Configuring max-bytes, max-lines, and the send queue length together is recommended to mitigate these issues.

Additionally, in the context of flood protection and throttling of incoming messages, keep in mind that clients might send an entire batched multiline message all at once.

Examples πŸ”—

This section is non-normative.

NOTE: In these examples, <SPACE> indicates a space character which would otherwise not be clearly visible.

Client sending a mutliline batch

Client: BATCH +123 draft/multiline #channel
Client: @batch=123 PRIVMSG #channel hello
Client: @batch=123 PRIVMSG #channel :
Client: @batch=123 privmsg #channel :how is<SPACE>
Client: @batch=123;draft/multiline-concat PRIVMSG #channel :everyone?
Client: BATCH -123

Server sending the same batch

Server: @msgid=xxx;account=account :n!u@h BATCH +123 draft/multiline #channel
Server: @batch=123 :n!u@h PRIVMSG #channel hello
Server: @batch=123 :n!u@h PRIVMSG #channel :
Server: @batch=123 :n!u@h PRIVMSG #channel :how is<SPACE>
Server: @batch=123;draft/multiline-concat :n!u@h PRIVMSG #channel :everyone?
Server: BATCH -123

Server sending messages to clients without multiline support

Server: @msgid=xxx;account=account :n!u@h PRIVMSG #channel hello
Server: @account=account :n!u@h PRIVMSG #channel :how is<SPACE>
Server: @account=account :n!u@h PRIVMSG #channel :everyone?

Final concatenated message


how is everyone?

This example is also valid if every instance of PRIVMSG is replaced with NOTICE.

Invalid multiline batch target

Client: BATCH +456 draft/multiline #foo
Client: @batch=456 PRIVMSG #bar hello
Client: BATCH -456

Server: FAIL BATCH MULTILINE_INVALID_TARGET #foo #bar :Invalid multiline target

max-bytes exceeded multiline batch

Server: CAP * LS :draft/multiline=max-bytes=40000

Client: BATCH +abc draft/multiline #channel
Client: @batch=abc PRIVMSG #channel hello
... lines that exceeed the max-bytes=40000 limit ...

Server: FAIL BATCH MULTILINE_MAX_BYTES 40000 :Multiline batch max-bytes exceeded

max-lines exceeded multiline batch

Server: CAP * LS :draft/multiline=max-bytes=40000,max-lines=10

Client: BATCH +abc draft/multiline #channel
Client: @batch=abc PRIVMSG #channel hello
... 10 more lines ...

Server: FAIL BATCH MULTILINE_MAX_LINES 10 :Multiline batch max-lines exceeded

Invalid use of a concatenated blank line

Client: BATCH +abc123 draft/multiline #channel
Client: @batch=abc123 PRIVMSG #channel :hello<space>
Client: @batch=abc123;draft/multiline-concat PRIVMSG #channel :
Client: @batch=abc123 PRIVMSG #channel :there
Client: BATCH -abc123

Server: FAIL BATCH MULTILINE_INVALID :Invalid multiline batch with concatenated blank line

Invalid entirely blank message

Client: BATCH +abc123 draft/multiline #channel
Client: @batch=abc123 PRIVMSG #channel :
Client: @batch=abc123 PRIVMSG #channel :
Client: BATCH -abc123

Server: FAIL BATCH MULTILINE_INVALID :Invalid multiline batch with blank lines only


Client: BATCH +792da7 draft/multiline #channel
Client: @batch=792da7 PRIVMSG #channel :this starts with a PRIVMSG
Client: @batch=792da7 NOTICE #channel :but ends with a NOTICE
Client: BATCH -792da7

Server: FAIL BATCH MULTILINE_INVALID :Invalid multiline batch

Other invalid multiline batch

Client: BATCH +999 draft/multiline #channel
... invalid batch contents ...

Server: FAIL BATCH MULTILINE_INVALID :Invalid multiline batch

Software supporting draft/multiline: Ergo, IRCCloud Teams, IRCCloud, IRCCloud (as Server), BitBot, Limnoria