refactor: model message schemas after IRC RFC 1459/2812

Replace c2s/s2c/s2s taxonomy with IRC-native structure:
- schema/commands/ — IRC command schemas (PRIVMSG, NOTICE, JOIN, PART,
  QUIT, NICK, TOPIC, MODE, KICK, PING, PONG)
- schema/numerics/ — IRC numeric reply codes (001-004, 322-323, 332,
  353, 366, 372-376, 401, 403, 433, 442, 482)
- schema/message.json — base envelope mapping IRC wire format to JSON

Messages use 'command' field with IRC command names or 3-digit numeric
codes. 'body' is a string (IRC trailing parameter), not object/array.
'from'/'to' map to IRC prefix and first parameter.

Federation uses the same IRC commands (no custom RELAY/LINK/SYNC).

Update README message format, command tables, and examples to match.
This commit is contained in:
clawbot
2026-02-10 10:31:26 -08:00
parent c8d88de8c5
commit dfb1636be5
72 changed files with 963 additions and 1149 deletions

View File

@@ -1,81 +1,87 @@
# Message Schema Index
# Message Schemas
JSON Schema (draft 2020-12) definitions for the IRC-style message protocol.
JSON Schema definitions (draft 2020-12) for the chat protocol. Messages use
**IRC command names and numeric reply codes** (RFC 1459/2812) encoded as JSON
over HTTP.
All messages share a common envelope defined in
[`message.schema.json`](message.schema.json).
## Envelope
## Base Envelope
Every message is a JSON object with a `command` field. The format maps directly
to IRC wire format:
| Field | Type | Required | Description |
|-----------|-----------------|----------|-------------|
| `command` | string | ✓ | IRC command name or numeric reply code |
| `from` | string | | Sender nick or server name |
| `to` | string | | Destination channel or nick |
| `params` | array\<string\> | | Additional IRC-style parameters |
| `body` | array \| object | varies | Message body (never a raw string) |
| `meta` | object | | Extensible metadata (signatures, etc.) |
| `id` | string (uuid) | | Server-assigned message ID |
| `ts` | string (date-time) | | Server-assigned timestamp |
```
IRC: :nick PRIVMSG #channel :hello world
JSON: {"command": "PRIVMSG", "from": "nick", "to": "#channel", "body": "hello world"}
## Client-to-Server (C2S)
IRC: :server 353 nick = #channel :user1 @op1 +voice1
JSON: {"command": "353", "to": "nick", "params": ["=", "#channel"], "body": "user1 @op1 +voice1"}
```
| Command | Schema | Description |
|----------|--------|-------------|
| PRIVMSG | [`c2s/privmsg.schema.json`](c2s/privmsg.schema.json) | Send message to channel or user |
| NOTICE | [`c2s/notice.schema.json`](c2s/notice.schema.json) | Send a notice |
| JOIN | [`c2s/join.schema.json`](c2s/join.schema.json) | Join a channel |
| PART | [`c2s/part.schema.json`](c2s/part.schema.json) | Leave a channel |
| QUIT | [`c2s/quit.schema.json`](c2s/quit.schema.json) | Disconnect |
| NICK | [`c2s/nick.schema.json`](c2s/nick.schema.json) | Change nick |
| MODE | [`c2s/mode.schema.json`](c2s/mode.schema.json) | Set/query modes |
| TOPIC | [`c2s/topic.schema.json`](c2s/topic.schema.json) | Set/query topic |
| KICK | [`c2s/kick.schema.json`](c2s/kick.schema.json) | Kick user |
| PING | [`c2s/ping.schema.json`](c2s/ping.schema.json) | Client keepalive |
| PUBKEY | [`c2s/pubkey.schema.json`](c2s/pubkey.schema.json) | Announce public key |
Common fields (see `message.json` for full schema):
## Server-to-Client (S2C)
| Field | Type | Description |
|-----------|----------|-------------------------------------------------------|
| `id` | integer | Server-assigned ID (monotonically increasing) |
| `command` | string | IRC command or 3-digit numeric code |
| `from` | string | Source nick or server name (IRC prefix) |
| `to` | string | Target: #channel or nick |
| `params` | string[] | Middle parameters (mainly for numerics) |
| `body` | string | Trailing parameter (message text) |
| `ts` | string | ISO 8601 timestamp (server-assigned, not in raw IRC) |
| `meta` | object | Extensible metadata (not in raw IRC) |
### Named Commands
## Commands
| Command | Schema | Description |
|----------|--------|-------------|
| PRIVMSG | [`s2c/privmsg.schema.json`](s2c/privmsg.schema.json) | Relayed message |
| NOTICE | [`s2c/notice.schema.json`](s2c/notice.schema.json) | Server or user notice |
| JOIN | [`s2c/join.schema.json`](s2c/join.schema.json) | User joined channel |
| PART | [`s2c/part.schema.json`](s2c/part.schema.json) | User left channel |
| QUIT | [`s2c/quit.schema.json`](s2c/quit.schema.json) | User disconnected |
| NICK | [`s2c/nick.schema.json`](s2c/nick.schema.json) | Nick change |
| MODE | [`s2c/mode.schema.json`](s2c/mode.schema.json) | Mode change |
| TOPIC | [`s2c/topic.schema.json`](s2c/topic.schema.json) | Topic change |
| KICK | [`s2c/kick.schema.json`](s2c/kick.schema.json) | User kicked |
| PONG | [`s2c/pong.schema.json`](s2c/pong.schema.json) | Server pong |
| PUBKEY | [`s2c/pubkey.schema.json`](s2c/pubkey.schema.json) | Relayed public key |
| ERROR | [`s2c/error.schema.json`](s2c/error.schema.json) | Server error |
IRC commands used for client↔server and server↔server communication.
### Numeric Replies
| Command | File | RFC | Description |
|-----------|---------------------------|-----------|--------------------------------|
| `PRIVMSG` | `commands/PRIVMSG.json` | 1459 §4.4.1 | Message to channel or user |
| `NOTICE` | `commands/NOTICE.json` | 1459 §4.4.2 | Notice (no auto-reply) |
| `JOIN` | `commands/JOIN.json` | 1459 §4.2.1 | Join a channel |
| `PART` | `commands/PART.json` | 1459 §4.2.2 | Leave a channel |
| `QUIT` | `commands/QUIT.json` | 1459 §4.1.6 | User disconnected |
| `NICK` | `commands/NICK.json` | 1459 §4.1.2 | Change nickname |
| `TOPIC` | `commands/TOPIC.json` | 1459 §4.2.4 | Get/set channel topic |
| `MODE` | `commands/MODE.json` | 1459 §4.2.3 | Set channel/user modes |
| `KICK` | `commands/KICK.json` | 1459 §4.2.8 | Kick user from channel |
| `PING` | `commands/PING.json` | 1459 §4.6.2 | Keepalive |
| `PONG` | `commands/PONG.json` | 1459 §4.6.3 | Keepalive response |
| Code | Name | Schema | Description |
|------|--------------------|--------|-------------|
| 001 | RPL_WELCOME | [`s2c/001.schema.json`](s2c/001.schema.json) | Welcome after registration |
| 002 | RPL_YOURHOST | [`s2c/002.schema.json`](s2c/002.schema.json) | Server host info |
| 322 | RPL_LIST | [`s2c/322.schema.json`](s2c/322.schema.json) | Channel list entry |
| 353 | RPL_NAMREPLY | [`s2c/353.schema.json`](s2c/353.schema.json) | Names list |
| 366 | RPL_ENDOFNAMES | [`s2c/366.schema.json`](s2c/366.schema.json) | End of names list |
| 372 | RPL_MOTD | [`s2c/372.schema.json`](s2c/372.schema.json) | MOTD line |
| 375 | RPL_MOTDSTART | [`s2c/375.schema.json`](s2c/375.schema.json) | Start of MOTD |
| 376 | RPL_ENDOFMOTD | [`s2c/376.schema.json`](s2c/376.schema.json) | End of MOTD |
| 401 | ERR_NOSUCHNICK | [`s2c/401.schema.json`](s2c/401.schema.json) | No such nick/channel |
| 403 | ERR_NOSUCHCHANNEL | [`s2c/403.schema.json`](s2c/403.schema.json) | No such channel |
| 433 | ERR_NICKNAMEINUSE | [`s2c/433.schema.json`](s2c/433.schema.json) | Nick in use |
## Numeric Replies
## Server-to-Server (S2S)
Three-digit codes for server responses, per IRC convention.
| Command | Schema | Description |
|---------|--------|-------------|
| RELAY | [`s2s/relay.schema.json`](s2s/relay.schema.json) | Relay message to linked server |
| LINK | [`s2s/link.schema.json`](s2s/link.schema.json) | Establish server link |
| UNLINK | [`s2s/unlink.schema.json`](s2s/unlink.schema.json) | Tear down link |
| SYNC | [`s2s/sync.schema.json`](s2s/sync.schema.json) | Synchronize state |
| PING | [`s2s/ping.schema.json`](s2s/ping.schema.json) | Server ping |
| PONG | [`s2s/pong.schema.json`](s2s/pong.schema.json) | Server pong |
### Success / Informational (0xx3xx)
| Code | Name | File | Description |
|-------|-------------------|-----------------------|--------------------------------|
| `001` | RPL_WELCOME | `numerics/001.json` | Welcome after session creation |
| `002` | RPL_YOURHOST | `numerics/002.json` | Server host info |
| `003` | RPL_CREATED | `numerics/003.json` | Server creation date |
| `004` | RPL_MYINFO | `numerics/004.json` | Server info and modes |
| `322` | RPL_LIST | `numerics/322.json` | Channel list entry |
| `323` | RPL_LISTEND | `numerics/323.json` | End of channel list |
| `332` | RPL_TOPIC | `numerics/332.json` | Channel topic |
| `353` | RPL_NAMREPLY | `numerics/353.json` | Channel member list |
| `366` | RPL_ENDOFNAMES | `numerics/366.json` | End of NAMES list |
| `372` | RPL_MOTD | `numerics/372.json` | MOTD line |
| `375` | RPL_MOTDSTART | `numerics/375.json` | Start of MOTD |
| `376` | RPL_ENDOFMOTD | `numerics/376.json` | End of MOTD |
### Errors (4xx)
| Code | Name | File | Description |
|-------|----------------------|-----------------------|--------------------------------|
| `401` | ERR_NOSUCHNICK | `numerics/401.json` | No such nick/channel |
| `403` | ERR_NOSUCHCHANNEL | `numerics/403.json` | No such channel |
| `433` | ERR_NICKNAMEINUSE | `numerics/433.json` | Nickname already in use |
| `442` | ERR_NOTONCHANNEL | `numerics/442.json` | Not on that channel |
| `482` | ERR_CHANOPRIVSNEEDED | `numerics/482.json` | Not channel operator |
## Federation (S2S)
Server-to-server messages use the same command format. Federated servers relay
messages with an additional `origin` field in `meta` to track the source server.
The PING/PONG commands serve as inter-server keepalives. State sync after link
establishment uses a burst of JOIN, NICK, TOPIC, and MODE commands.