Files
neoirc/schema/README.md
T
clawbot 58efb7d476
check / check (push) Waiting to run
Rewrite schema/README.md sentence by sentence
Rewrite the description of the JSON Schema directory from scratch,
without tables, as a reader of the files needs it. Describe the
envelope as the server sends it: the reply name in `command` and the
number in `code`. Drop the federation section, which describes nothing
the server does. List where the schema files disagree with the server,
compared file by file with the HTTP API, so a reader is not misled; the
files themselves are unchanged.

Part of #190.

Model: fable-5-1 (writing); opus-5-5 (rework)
2026-10-27 14:32:22 +00:00

7.6 KiB

Message schemas

This directory holds JSON Schema files (draft 2020-12) for the messages that the neoirc HTTP API carries. message.json describes the envelope every message uses. commands/ has files for some of the IRC commands. numerics/ has files for some of the numeric replies. README.md at the repository root documents the protocol.

The envelope

Every message is one JSON object. These are the fields it can have:

  • command: the IRC command name, such as PRIVMSG, or the name of a numeric reply, such as RPL_WELCOME. Always present.
  • code: the number of a numeric reply, such as 1 for RPL_WELCOME. Absent on other messages.
  • id: the message's UUID, assigned by the server.
  • from: the sender's nick, or the server name on a server message.
  • to: the target, such as a channel name starting with # or a nick.
  • params: other parameters, in order, as an array of strings. KICK events and many numeric replies have it. The server ignores params sent by a client.
  • body: an array of strings. It is never a bare string. In a PRIVMSG or NOTICE, each string is one line of text.
  • ts: when the server stored the message, as an RFC 3339 time in UTC, such as 2026-10-27T12:00:00.123Z.
  • meta: an object of extra data, such as a hashcash stamp in meta.hashcash. A hashcash stamp is a short string that takes a computer some work to make. A channel with mode +H needs one in every PRIVMSG. The server relays the meta of a PRIVMSG or NOTICE unchanged. It drops the meta of every other command.

A PRIVMSG as the IRC listener sends it, and as the HTTP API sends it without id, ts and meta:

IRC:  :alice!alice@* PRIVMSG #general :hello world
JSON: {"command": "PRIVMSG", "from": "alice", "to": "#general", "body": ["hello world"]}

A numeric reply has its name in command and its number in code. This 353 reply lists the members of #general, again without id, ts and meta:

{"command": "RPL_NAMREPLY", "code": 353, "from": "neoirc", "to": "alice",
 "params": ["=", "#general"], "body": ["@op1!op1@host1 alice!alice@host2"]}

A body with two strings is a two-line message:

{"command": "PRIVMSG", "to": "#general", "body": ["line one", "line two"]}

Files

Commands, in commands/:

  • PRIVMSG.json: a message to a channel or a user.
  • NOTICE.json: a notice. A client must not answer a notice automatically.
  • JOIN.json: a user joined a channel.
  • PART.json: a user left a channel.
  • QUIT.json: a user left the server.
  • NICK.json: a user changed nick.
  • TOPIC.json: a channel topic was set.
  • MODE.json: a channel mode was changed.
  • KICK.json: a channel operator removed a user from a channel.
  • PING.json: a check that the server still answers.
  • PONG.json: the answer to a PING.
  • PUBKEY.json: an announcement of a user's public signing key. The server has no such command. It answers a PUBKEY with ERR_UNKNOWNCOMMAND (421).

Numeric replies, in numerics/:

  • 001.json: RPL_WELCOME, which welcomes a new user.
  • 002.json: RPL_YOURHOST, the server's name and version.
  • 003.json: RPL_CREATED, the date the server started.
  • 004.json: RPL_MYINFO, the server's version and its lists of modes.
  • 322.json: RPL_LIST, one channel in the answer to LIST.
  • 323.json: RPL_LISTEND, the end of the answer to LIST.
  • 332.json: RPL_TOPIC, a channel's topic.
  • 353.json: RPL_NAMREPLY, a channel's members.
  • 366.json: RPL_ENDOFNAMES, the end of the member list.
  • 372.json: RPL_MOTD, one line of the message of the day.
  • 375.json: RPL_MOTDSTART, the start of the message of the day.
  • 376.json: RPL_ENDOFMOTD, the end of the message of the day.
  • 401.json: ERR_NOSUCHNICK, the nick does not exist.
  • 403.json: ERR_NOSUCHCHANNEL, the channel does not exist.
  • 433.json: ERR_NICKNAMEINUSE, the nick is taken.
  • 442.json: ERR_NOTONCHANNEL, the user is not on that channel.
  • 482.json: ERR_CHANOPRIVSNEEDED, the user is not a channel operator.

Where the files differ from the server

Where a schema file and the server disagree, the server is right:

  • message.json and the numeric files put the three-digit code in command and have no code field. The server puts the reply's name in command and its number in code.
  • message.json allows an object as body. The server reads only arrays of strings. A PRIVMSG with an object gets ERR_NOTEXTTOSEND (412).
  • message.json says to is a channel or a nick. A WALLOPS message has * there.
  • message.json allows meta on every message. The server keeps meta only on a PRIVMSG or NOTICE.
  • message.json describes signature fields in meta. The server never checks a signature.
  • message.json does not describe meta.hashcash. The server reads it in a PRIVMSG to a channel with mode +H.
  • JOIN.json has no body. A client's JOIN can carry the channel key in body. When a user joins, the server's JOIN event repeats the channel name there.
  • JOIN.json allows a channel name of any length. The server allows at most 63 characters after the #. It answers a longer name with ERR_NOSUCHCHANNEL (403).
  • KICK.json puts the kicked nick in params. The server reads a client's KICK from body: the nick, then an optional reason.
  • MODE.json puts the mode change in params. The server uses body instead. A client sends the change and any value as separate strings, such as "+o" and "bob". The server's MODE event has one string such as "+o bob", or the change and its value as two strings, such as "+k" and "secret".
  • NOTICE.json allows * as to. The server answers that with ERR_NOSUCHNICK (401).
  • NOTICE.json has an example notice from the server to *. The server sends no such notice.
  • PING.json and PONG.json describe a token that the PONG repeats. The server ignores the body of a PING. It answers the HTTP request itself with {"command": "PONG", "from": "<server name>"}.
  • PING.json and PONG.json also describe messages between servers. The server does not connect to other IRC servers.
  • PUBKEY.json describes a command the server does not have.
  • QUIT.json says only the server sends QUIT. A client can send it too, to end its session. The client's QUIT can carry a reason in body.
  • TOPIC.json says a TOPIC without a body queries the topic. Over the HTTP API a TOPIC without a body gets ERR_NEEDMOREPARAMS (461).
  • TOPIC.json says an empty array clears the topic. The server answers [] with ERR_NEEDMOREPARAMS (461). A body of [""] clears the topic.
  • 003.json describes the server's creation date. The server sends the date it last started.
  • 004.json has example mode lists. The server sends an empty string as the user modes and ikmnostl as the channel modes.
  • 322.json says LIST gets one reply per channel. The server leaves out each secret channel that the user is not on.
  • 353.json says body lists nicks. Over the HTTP API the server lists nick!user@host in place of each nick, such as @op1!op1@host1.
  • 353.json says the first parameter marks a channel as public, private or secret. The server always sends =, the mark for a public channel.
  • 376.json gives the text as End of /MOTD command. The server adds a full stop.
  • 401.json gives the text as No such nick/channel. For a PRIVMSG or NOTICE to an unknown nick, the server sends No such nick.
  • 433.json has an example with * as to. Over the HTTP API the server sends 433 only in answer to a NICK, with the user's nick as to.

The server also accepts some messages that the files do not allow, such as a channel name without its #.