Files
neoirc/schema/README.md
T
clawbot 89df63f056
check / check (push) Waiting to run
Rewrite schema/README.md sentence by sentence
Rewrite the description of the JSON Schema files from scratch, without
tables. Describe the fields of a message as the HTTP API sends them:
the reply name in `command` and its number in `code`. Drop the
federation section, which describes nothing the server does. List each
place where a schema file disagrees with the HTTP API, so that 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:58:22 +00:00

8.3 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 fields that every message can have. commands/ has files for some commands. numerics/ has files for some of the numeric replies. A numeric reply is a server reply named by a three-digit number, such as 001. README.md at the repository root documents the protocol.

The server can also run an IRC listener for ordinary IRC clients. The IRC listener sends plain IRC lines, which these files do not describe.

Message fields

Every message is one JSON object, which message.json calls the envelope. 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, a unique ID that the server assigns.
  • 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: an array of strings with the message's other parameters, in order. KICK events and many numeric replies have it. The HTTP API 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: the time the server stored the message. It is 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 PRIVMSG to a channel with mode +H needs one. The HTTP API relays the meta of a PRIVMSG or NOTICE unchanged. It drops the meta of every other command.

Here is one PRIVMSG as the IRC listener sends it and as the HTTP API sends it. The JSON leaves out id, ts and meta:

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

In the JSON, a numeric reply has its name in command and its number in code. This 353 reply lists the members of #general. The JSON again leaves out id, ts and meta:

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

A PRIVMSG whose body has 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, which is a message that a client must not answer 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, which says when the server was created.
  • 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 HTTP API

Where a schema file and the HTTP API disagree, the HTTP API is right:

  • message.json and the files in numerics/ put the three-digit code in command. They have no code field. The HTTP API puts the reply's name in command and its number in code.
  • message.json allows an object as body. The HTTP API reads only arrays of strings. A PRIVMSG with an object as body 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 HTTP API keeps a client's 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 HTTP API 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 JOIN event repeats the channel name in body.
  • 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 HTTP API reads a client's KICK from body: the nick, then an optional reason.
  • MODE.json puts the mode change in params. The HTTP API uses body instead. A client sends the change and any value as separate strings, such as "+o" and "bob". In a MODE event, body holds the change as one string, such as "+o bob", or as two strings, such as "+k" and "secret".
  • NOTICE.json allows * as to. The server answers a NOTICE to * 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 HTTP API ignores the body of a PING. Its answer is the HTTP response {"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 that QUIT comes only from the server. A client can send it too, to end its session. A client's QUIT can carry a reason in body.
  • TOPIC.json says a TOPIC without a body queries the topic. The HTTP API answers such a TOPIC with ERR_NEEDMOREPARAMS (461).
  • TOPIC.json says an empty array clears the topic. The HTTP API answers [] with ERR_NEEDMOREPARAMS (461). A body of [""] clears the topic.
  • 003.json describes the server's creation date. The HTTP API sends the date the server last started.
  • 004.json has example mode lists. The HTTP API 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 a secret channel, one with mode +s, unless the user is on it.
  • 332.json says RPL_TOPIC answers a TOPIC query. The HTTP API has no such query. It sends RPL_TOPIC after a client sets the topic, and after a JOIN to a channel that has one.
  • 353.json says body lists nicks. The HTTP API 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 HTTP API adds a full stop.
  • 401.json gives the text as No such nick/channel. The server sends No such nick in answer to a PRIVMSG or NOTICE to an unknown nick.
  • 433.json has an example with * as to. The HTTP API sends 433 only in answer to a NICK, with the user's nick as to.

The HTTP API also accepts some messages that the files do not allow, such as a JOIN whose to has no #.