# 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": ""}`. - `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 `#`.