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)
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 asPRIVMSG, or the name of a numeric reply, such asRPL_WELCOME. Always present.code: the number of a numeric reply, such as1forRPL_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.KICKevents and many numeric replies have it. The HTTP API ignoresparamssent by a client.body: an array of strings. It is never a bare string. In aPRIVMSGorNOTICE, each string is one line of text.ts: the time the server stored the message. It is an RFC 3339 time in UTC, such as2026-10-27T12:00:00.123Z.meta: an object of extra data, such as a hashcash stamp inmeta.hashcash. A hashcash stamp is a short string that takes a computer some work to make. APRIVMSGto a channel with mode+Hneeds one. The HTTP API relays themetaof aPRIVMSGorNOTICEunchanged. It drops themetaof 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 aPING.PUBKEY.json: an announcement of a user's public signing key. The server has no such command. It answers aPUBKEYwithERR_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 toLIST.323.json:RPL_LISTEND, the end of the answer toLIST.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.jsonand the files innumerics/put the three-digit code incommand. They have nocodefield. The HTTP API puts the reply's name incommandand its number incode.message.jsonallows an object asbody. The HTTP API reads only arrays of strings. APRIVMSGwith an object asbodygetsERR_NOTEXTTOSEND(412).message.jsonsaystois a channel or a nick. AWALLOPSmessage has*there.message.jsonallowsmetaon every message. The HTTP API keeps a client'smetaonly on aPRIVMSGorNOTICE.message.jsondescribes signature fields inmeta. The server never checks a signature.message.jsondoes not describemeta.hashcash. The HTTP API reads it in aPRIVMSGto a channel with mode+H.JOIN.jsonhas nobody. A client'sJOINcan carry the channel key inbody. When a user joins, theJOINevent repeats the channel name inbody.JOIN.jsonallows a channel name of any length. The server allows at most 63 characters after the#. It answers a longer name withERR_NOSUCHCHANNEL(403).KICK.jsonputs the kicked nick inparams. The HTTP API reads a client'sKICKfrombody: the nick, then an optional reason.MODE.jsonputs the mode change inparams. The HTTP API usesbodyinstead. A client sends the change and any value as separate strings, such as"+o"and"bob". In aMODEevent,bodyholds the change as one string, such as"+o bob", or as two strings, such as"+k"and"secret".NOTICE.jsonallows*asto. The server answers aNOTICEto*withERR_NOSUCHNICK(401).NOTICE.jsonhas an example notice from the server to*. The server sends no such notice.PING.jsonandPONG.jsondescribe a token that thePONGrepeats. The HTTP API ignores thebodyof aPING. Its answer is the HTTP response{"command": "PONG", "from": "<server name>"}.PING.jsonandPONG.jsonalso describe messages between servers. The server does not connect to other IRC servers.PUBKEY.jsondescribes a command the server does not have.QUIT.jsonsays thatQUITcomes only from the server. A client can send it too, to end its session. A client'sQUITcan carry a reason inbody.TOPIC.jsonsays aTOPICwithout abodyqueries the topic. The HTTP API answers such aTOPICwithERR_NEEDMOREPARAMS(461).TOPIC.jsonsays an empty array clears the topic. The HTTP API answers[]withERR_NEEDMOREPARAMS(461). Abodyof[""]clears the topic.003.jsondescribes the server's creation date. The HTTP API sends the date the server last started.004.jsonhas example mode lists. The HTTP API sends an empty string as the user modes andikmnostlas the channel modes.322.jsonsaysLISTgets one reply per channel. The server leaves out a secret channel, one with mode+s, unless the user is on it.332.jsonsaysRPL_TOPICanswers aTOPICquery. The HTTP API has no such query. It sendsRPL_TOPICafter a client sets the topic, and after aJOINto a channel that has one.353.jsonsaysbodylists nicks. The HTTP API listsnick!user@hostin place of each nick, such as@op1!op1@host1.353.jsonsays the first parameter marks a channel as public, private or secret. The server always sends=, the mark for a public channel.376.jsongives the text asEnd of /MOTD command. The HTTP API adds a full stop.401.jsongives the text asNo such nick/channel. The server sendsNo such nickin answer to aPRIVMSGorNOTICEto an unknown nick.433.jsonhas an example with*asto. The HTTP API sends433only in answer to aNICK, with the user's nick asto.
The HTTP API also accepts some messages that the files do not allow, such as a
JOIN whose to has no #.