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.

View File

@@ -1,23 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/join.schema.json",
"title": "JOIN (C2S)",
"description": "Join a channel",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "JOIN"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Not used"
}
},
"required": [
"command",
"to"
]
}

View File

@@ -1,23 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/kick.schema.json",
"title": "KICK (C2S)",
"description": "Kick user from channel",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "KICK"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Kick reason"
}
},
"required": [
"command",
"to"
]
}

View File

@@ -1,23 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/mode.schema.json",
"title": "MODE (C2S)",
"description": "Set/query modes",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "MODE"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Mode params"
}
},
"required": [
"command",
"to"
]
}

View File

@@ -1,23 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/nick.schema.json",
"title": "NICK (C2S)",
"description": "Request nick change",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "NICK"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Not used"
}
},
"required": [
"command",
"to"
]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/notice.schema.json",
"title": "NOTICE (C2S)",
"description": "Send a notice (no auto-reply expected)",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "NOTICE"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Text lines"
}
},
"required": [
"command",
"to",
"body"
]
}

View File

@@ -1,23 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/part.schema.json",
"title": "PART (C2S)",
"description": "Leave a channel",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "PART"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Part message"
}
},
"required": [
"command",
"to"
]
}

View File

@@ -1,22 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/ping.schema.json",
"title": "PING (C2S)",
"description": "Client keepalive",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "PING"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ping token"
}
},
"required": [
"command"
]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/privmsg.schema.json",
"title": "PRIVMSG (C2S)",
"description": "Send message to channel or user",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "PRIVMSG"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Text lines"
}
},
"required": [
"command",
"to",
"body"
]
}

View File

@@ -1,33 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/pubkey.schema.json",
"title": "PUBKEY (C2S)",
"description": "Announce public signing key",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "PUBKEY"
},
"body": {
"type": "object",
"required": [
"alg",
"key"
],
"properties": {
"alg": {
"type": "string",
"description": "Key algorithm (e.g. ed25519)"
},
"key": {
"type": "string",
"description": "Base64-encoded public key"
}
}
}
},
"required": [
"command",
"body"
]
}

View File

@@ -1,22 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/quit.schema.json",
"title": "QUIT (C2S)",
"description": "Disconnect from server",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "QUIT"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Quit message"
}
},
"required": [
"command"
]
}

View File

@@ -1,23 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/topic.schema.json",
"title": "TOPIC (C2S)",
"description": "Set/query topic",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "TOPIC"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Topic lines"
}
},
"required": [
"command",
"to"
]
}

16
schema/commands/JOIN.json Normal file
View File

@@ -0,0 +1,16 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/commands/JOIN.json",
"title": "JOIN",
"description": "Join a channel. C2S: request to join. S2C: notification that a user joined. RFC 1459 §4.2.1.",
"$ref": "../message.json",
"properties": {
"command": { "const": "JOIN" },
"from": { "type": "string", "description": "Nick that joined (S2C)." },
"to": { "type": "string", "description": "Channel name.", "pattern": "^#[a-zA-Z0-9_-]+$" }
},
"required": ["command", "to"],
"examples": [
{ "command": "JOIN", "from": "alice", "to": "#general" }
]
}

34
schema/commands/KICK.json Normal file
View File

@@ -0,0 +1,34 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/commands/KICK.json",
"title": "KICK",
"description": "Kick a user from a channel. RFC 1459 §4.2.8.",
"$ref": "../message.json",
"properties": {
"command": { "const": "KICK" },
"from": {
"type": "string",
"description": "Nick that performed the kick."
},
"to": {
"type": "string",
"description": "Channel name.",
"pattern": "^#[a-zA-Z0-9_-]+$"
},
"params": {
"type": "array",
"items": { "type": "string" },
"description": "Kicked nick. e.g. [\"alice\"].",
"minItems": 1,
"maxItems": 1
},
"body": {
"type": "string",
"description": "Optional kick reason."
}
},
"required": ["command", "to", "params"],
"examples": [
{ "command": "KICK", "from": "op1", "to": "#general", "params": ["troll"], "body": "Behave" }
]
}

29
schema/commands/MODE.json Normal file
View File

@@ -0,0 +1,29 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/commands/MODE.json",
"title": "MODE",
"description": "Set or query channel/user modes. RFC 1459 §4.2.3.",
"$ref": "../message.json",
"properties": {
"command": { "const": "MODE" },
"from": {
"type": "string",
"description": "Nick that set the mode (S2C only)."
},
"to": {
"type": "string",
"description": "Channel name.",
"pattern": "^#[a-zA-Z0-9_-]+$"
},
"params": {
"type": "array",
"items": { "type": "string" },
"description": "Mode string and optional target nick. e.g. [\"+o\", \"alice\"].",
"examples": [["+o", "alice"], ["-m"], ["+v", "bob"]]
}
},
"required": ["command", "to", "params"],
"examples": [
{ "command": "MODE", "from": "op1", "to": "#general", "params": ["+o", "alice"] }
]
}

16
schema/commands/NICK.json Normal file
View File

@@ -0,0 +1,16 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/commands/NICK.json",
"title": "NICK",
"description": "Change nickname. C2S: request new nick. S2C: notification of nick change. RFC 1459 §4.1.2.",
"$ref": "../message.json",
"properties": {
"command": { "const": "NICK" },
"from": { "type": "string", "description": "Old nick (S2C)." },
"body": { "type": "string", "description": "New nick.", "minLength": 1, "maxLength": 32, "pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$" }
},
"required": ["command", "body"],
"examples": [
{ "command": "NICK", "from": "oldnick", "body": "newnick" }
]
}

View File

@@ -0,0 +1,17 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/commands/NOTICE.json",
"title": "NOTICE",
"description": "Send a notice. Like PRIVMSG but must not trigger automatic replies. RFC 1459 §4.4.2.",
"$ref": "../message.json",
"properties": {
"command": { "const": "NOTICE" },
"from": { "type": "string" },
"to": { "type": "string", "description": "Target: #channel, nick, or * (global)." },
"body": { "type": "string", "description": "Notice text." }
},
"required": ["command", "to", "body"],
"examples": [
{ "command": "NOTICE", "from": "server.example.com", "to": "*", "body": "Server restarting in 5 minutes" }
]
}

17
schema/commands/PART.json Normal file
View File

@@ -0,0 +1,17 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/commands/PART.json",
"title": "PART",
"description": "Leave a channel. C2S: request to leave. S2C: notification that a user left. RFC 1459 §4.2.2.",
"$ref": "../message.json",
"properties": {
"command": { "const": "PART" },
"from": { "type": "string", "description": "Nick that left (S2C)." },
"to": { "type": "string", "description": "Channel name.", "pattern": "^#[a-zA-Z0-9_-]+$" },
"body": { "type": "string", "description": "Optional part reason." }
},
"required": ["command", "to"],
"examples": [
{ "command": "PART", "from": "alice", "to": "#general", "body": "later" }
]
}

18
schema/commands/PING.json Normal file
View File

@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/commands/PING.json",
"title": "PING",
"description": "Keepalive. C2S or S2S. Server responds with PONG. RFC 1459 §4.6.2.",
"$ref": "../message.json",
"properties": {
"command": { "const": "PING" },
"body": {
"type": "string",
"description": "Opaque token to be echoed in PONG."
}
},
"required": ["command"],
"examples": [
{ "command": "PING", "body": "1707580000" }
]
}

22
schema/commands/PONG.json Normal file
View File

@@ -0,0 +1,22 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/commands/PONG.json",
"title": "PONG",
"description": "Keepalive response. S2C or S2S. RFC 1459 §4.6.3.",
"$ref": "../message.json",
"properties": {
"command": { "const": "PONG" },
"from": {
"type": "string",
"description": "Responding server name."
},
"body": {
"type": "string",
"description": "Echoed token from PING."
}
},
"required": ["command"],
"examples": [
{ "command": "PONG", "from": "server.example.com", "body": "1707580000" }
]
}

View File

@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/commands/PRIVMSG.json",
"title": "PRIVMSG",
"description": "Send a message to a channel or user. C2S: client sends to server. S2C: server relays to recipients. RFC 1459 §4.4.1.",
"$ref": "../message.json",
"properties": {
"command": { "const": "PRIVMSG" },
"from": { "type": "string", "description": "Sender nick (set by server on relay)." },
"to": { "type": "string", "description": "Target: #channel or nick.", "examples": ["#general", "alice"] },
"body": { "type": "string", "description": "Message text.", "minLength": 1 }
},
"required": ["command", "to", "body"],
"examples": [
{ "command": "PRIVMSG", "from": "bob", "to": "#general", "body": "hello world" },
{ "command": "PRIVMSG", "from": "bob", "to": "alice", "body": "hey" }
]
}

16
schema/commands/QUIT.json Normal file
View File

@@ -0,0 +1,16 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/commands/QUIT.json",
"title": "QUIT",
"description": "User disconnected. S2C only. RFC 1459 §4.1.6.",
"$ref": "../message.json",
"properties": {
"command": { "const": "QUIT" },
"from": { "type": "string", "description": "Nick that quit." },
"body": { "type": "string", "description": "Optional quit reason." }
},
"required": ["command", "from"],
"examples": [
{ "command": "QUIT", "from": "alice", "body": "Connection reset" }
]
}

View File

@@ -0,0 +1,17 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/commands/TOPIC.json",
"title": "TOPIC",
"description": "Get or set channel topic. C2S: set topic (body present) or query (body absent). S2C: topic change notification. RFC 1459 §4.2.4.",
"$ref": "../message.json",
"properties": {
"command": { "const": "TOPIC" },
"from": { "type": "string", "description": "Nick that changed the topic (S2C)." },
"to": { "type": "string", "description": "Channel name.", "pattern": "^#[a-zA-Z0-9_-]+$" },
"body": { "type": "string", "description": "New topic text. Empty string clears the topic.", "maxLength": 512 }
},
"required": ["command", "to"],
"examples": [
{ "command": "TOPIC", "from": "alice", "to": "#general", "body": "Welcome to the chat" }
]
}

46
schema/message.json Normal file
View File

@@ -0,0 +1,46 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/message.json",
"title": "IRC Message Envelope",
"description": "Base envelope for all messages. Mirrors IRC wire format (RFC 1459/2812) encoded as JSON over HTTP. The 'command' field carries either an IRC command name (PRIVMSG, JOIN, etc.) or a three-digit numeric reply code (001, 353, 433, etc.).",
"type": "object",
"properties": {
"id": {
"type": "integer",
"description": "Server-assigned message ID, monotonically increasing. Present on all server-originated messages."
},
"command": {
"type": "string",
"description": "IRC command name (PRIVMSG, JOIN, NICK, etc.) or three-digit numeric reply code (001, 353, 433, etc.).",
"examples": ["PRIVMSG", "JOIN", "001", "353", "433"]
},
"from": {
"type": "string",
"description": "Source — nick for user messages, server name for server messages. Equivalent to IRC prefix."
},
"to": {
"type": "string",
"description": "Target — channel (#name) or nick. Equivalent to first IRC parameter for most commands."
},
"params": {
"type": "array",
"items": { "type": "string" },
"description": "Additional parameters (used primarily by numeric replies). Equivalent to IRC middle parameters."
},
"body": {
"type": "string",
"description": "Message body / trailing parameter. Equivalent to IRC trailing parameter (after the colon)."
},
"ts": {
"type": "string",
"format": "date-time",
"description": "Server-assigned timestamp (ISO 8601). Not present in original IRC; added for HTTP transport."
},
"meta": {
"type": "object",
"description": "Extensible metadata (signatures, rich content hints, etc.). Not present in original IRC.",
"additionalProperties": true
}
},
"required": ["command"]
}

View File

@@ -1,67 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/message.schema.json",
"title": "Chat Message Envelope",
"description": "Base message envelope. Bodies MUST be objects or arrays (never raw strings) for deterministic canonicalization (RFC 8785 JCS) and signing.",
"type": "object",
"required": [
"command"
],
"properties": {
"id": {
"type": "string",
"format": "uuid",
"description": "Server-assigned UUID"
},
"ts": {
"type": "string",
"format": "date-time",
"description": "Server-assigned timestamp (ISO 8601)"
},
"command": {
"type": "string",
"description": "IRC command name or numeric reply code"
},
"from": {
"type": "string",
"description": "Sender nick or server name"
},
"to": {
"type": "string",
"description": "Destination: channel (#foo) or nick"
},
"params": {
"type": "array",
"items": {
"type": "string"
},
"description": "Additional IRC-style parameters"
},
"body": {
"oneOf": [
{
"type": "array"
},
{
"type": "object"
}
],
"description": "Message body (array or object, never raw string)"
},
"meta": {
"type": "object",
"description": "Extensible metadata",
"properties": {
"sig": {
"type": "string",
"description": "Cryptographic signature (base64)"
},
"alg": {
"type": "string",
"description": "Signature algorithm (e.g. ed25519)"
}
}
}
},
"additionalProperties": false
}

20
schema/numerics/001.json Normal file
View File

@@ -0,0 +1,20 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/numerics/001.json",
"title": "001 RPL_WELCOME",
"description": "Welcome message sent after successful session creation. RFC 2812 §5.1.",
"$ref": "../message.json",
"properties": {
"command": { "const": "001" },
"to": { "type": "string", "description": "Target nick." },
"body": {
"type": "array",
"items": { "type": "string" },
"description": "Welcome text lines."
}
},
"required": ["command", "to", "body"],
"examples": [
{ "command": "001", "to": "alice", "body": ["Welcome to the network, alice"] }
]
}

20
schema/numerics/002.json Normal file
View File

@@ -0,0 +1,20 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/numerics/002.json",
"title": "002 RPL_YOURHOST",
"description": "Server host info sent after session creation. RFC 2812 §5.1.",
"$ref": "../message.json",
"properties": {
"command": { "const": "002" },
"to": { "type": "string" },
"body": {
"type": "array",
"items": { "type": "string" },
"description": "Host info lines."
}
},
"required": ["command", "to", "body"],
"examples": [
{ "command": "002", "to": "alice", "body": ["Your host is chat.example.com, running version 0.1.0"] }
]
}

36
schema/numerics/003.json Normal file
View File

@@ -0,0 +1,36 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/numerics/003.json",
"title": "003 RPL_CREATED",
"description": "Server creation date. RFC 2812 \u00a75.1.",
"$ref": "../message.json",
"properties": {
"command": {
"const": "003"
},
"to": {
"type": "string"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Text lines."
}
},
"required": [
"command",
"to",
"body"
],
"examples": [
{
"command": "003",
"to": "alice",
"body": [
"This server was created 2026-02-01"
]
}
]
}

39
schema/numerics/004.json Normal file
View File

@@ -0,0 +1,39 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/numerics/004.json",
"title": "004 RPL_MYINFO",
"description": "Server info (name, version, available modes). RFC 2812 \u00a75.1.",
"$ref": "../message.json",
"properties": {
"command": {
"const": "004"
},
"to": {
"type": "string"
},
"params": {
"type": "array",
"items": {
"type": "string"
},
"description": "[server_name, version, user_modes, channel_modes]."
}
},
"required": [
"command",
"to",
"params"
],
"examples": [
{
"command": "004",
"to": "alice",
"params": [
"chat.example.com",
"0.1.0",
"o",
"imnst+ov"
]
}
]
}

47
schema/numerics/322.json Normal file
View File

@@ -0,0 +1,47 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/numerics/322.json",
"title": "322 RPL_LIST",
"description": "Channel list entry. One per channel in response to LIST. RFC 1459 \u00a76.2.",
"$ref": "../message.json",
"properties": {
"command": {
"const": "322"
},
"to": {
"type": "string"
},
"params": {
"type": "array",
"items": {
"type": "string"
},
"description": "[channel, visible_count]."
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Channel topic."
}
},
"required": [
"command",
"to",
"params"
],
"examples": [
{
"command": "322",
"to": "alice",
"params": [
"#general",
"12"
],
"body": [
"General discussion"
]
}
]
}

13
schema/numerics/323.json Normal file
View File

@@ -0,0 +1,13 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/numerics/323.json",
"title": "323 RPL_LISTEND",
"description": "End of channel list. RFC 1459 §6.2.",
"$ref": "../message.json",
"properties": {
"command": { "const": "323" },
"to": { "type": "string" },
"body": { "const": "End of /LIST" }
},
"required": ["command", "to"]
}

47
schema/numerics/332.json Normal file
View File

@@ -0,0 +1,47 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/numerics/332.json",
"title": "332 RPL_TOPIC",
"description": "Channel topic (sent on JOIN or TOPIC query). RFC 1459 \u00a76.2.",
"$ref": "../message.json",
"properties": {
"command": {
"const": "332"
},
"to": {
"type": "string"
},
"params": {
"type": "array",
"items": {
"type": "string"
},
"description": "[channel]."
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Topic text."
}
},
"required": [
"command",
"to",
"params",
"body"
],
"examples": [
{
"command": "332",
"to": "alice",
"params": [
"#general"
],
"body": [
"Welcome to the chat"
]
}
]
}

48
schema/numerics/353.json Normal file
View File

@@ -0,0 +1,48 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/numerics/353.json",
"title": "353 RPL_NAMREPLY",
"description": "Channel member list. Sent on JOIN or NAMES query. RFC 1459 \u00a76.2.",
"$ref": "../message.json",
"properties": {
"command": {
"const": "353"
},
"to": {
"type": "string"
},
"params": {
"type": "array",
"items": {
"type": "string"
},
"description": "[channel_type, channel]. channel_type: = (public), * (private), @ (secret)."
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Space-separated list of nicks. Prefixed with @ for ops, + for voiced."
}
},
"required": [
"command",
"to",
"params",
"body"
],
"examples": [
{
"command": "353",
"to": "alice",
"params": [
"=",
"#general"
],
"body": [
"@op1 alice bob +voiced1"
]
}
]
}

18
schema/numerics/366.json Normal file
View File

@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/numerics/366.json",
"title": "366 RPL_ENDOFNAMES",
"description": "End of NAMES list. RFC 1459 §6.2.",
"$ref": "../message.json",
"properties": {
"command": { "const": "366" },
"to": { "type": "string" },
"params": {
"type": "array",
"items": { "type": "string" },
"description": "[channel]."
},
"body": { "const": "End of /NAMES list" }
},
"required": ["command", "to", "params"]
}

36
schema/numerics/372.json Normal file
View File

@@ -0,0 +1,36 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/numerics/372.json",
"title": "372 RPL_MOTD",
"description": "MOTD line. One message per line of the MOTD. RFC 2812 \u00a75.1.",
"$ref": "../message.json",
"properties": {
"command": {
"const": "372"
},
"to": {
"type": "string"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "MOTD line text (prefixed with '- ')."
}
},
"required": [
"command",
"to",
"body"
],
"examples": [
{
"command": "372",
"to": "alice",
"body": [
"- Welcome to our server!"
]
}
]
}

26
schema/numerics/375.json Normal file
View File

@@ -0,0 +1,26 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/numerics/375.json",
"title": "375 RPL_MOTDSTART",
"description": "Start of MOTD. RFC 2812 \u00a75.1.",
"$ref": "../message.json",
"properties": {
"command": {
"const": "375"
},
"to": {
"type": "string"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Text lines."
}
},
"required": [
"command",
"to"
]
}

13
schema/numerics/376.json Normal file
View File

@@ -0,0 +1,13 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/numerics/376.json",
"title": "376 RPL_ENDOFMOTD",
"description": "End of MOTD. RFC 2812 §5.1.",
"$ref": "../message.json",
"properties": {
"command": { "const": "376" },
"to": { "type": "string" },
"body": { "const": "End of /MOTD command" }
},
"required": ["command", "to"]
}

21
schema/numerics/401.json Normal file
View File

@@ -0,0 +1,21 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/numerics/401.json",
"title": "401 ERR_NOSUCHNICK",
"description": "No such nick/channel. RFC 1459 §6.1.",
"$ref": "../message.json",
"properties": {
"command": { "const": "401" },
"to": { "type": "string" },
"params": {
"type": "array",
"items": { "type": "string" },
"description": "[target_nick]."
},
"body": { "const": "No such nick/channel" }
},
"required": ["command", "to", "params"],
"examples": [
{ "command": "401", "to": "alice", "params": ["bob"], "body": "No such nick/channel" }
]
}

21
schema/numerics/403.json Normal file
View File

@@ -0,0 +1,21 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/numerics/403.json",
"title": "403 ERR_NOSUCHCHANNEL",
"description": "No such channel. RFC 1459 §6.1.",
"$ref": "../message.json",
"properties": {
"command": { "const": "403" },
"to": { "type": "string" },
"params": {
"type": "array",
"items": { "type": "string" },
"description": "[channel_name]."
},
"body": { "const": "No such channel" }
},
"required": ["command", "to", "params"],
"examples": [
{ "command": "403", "to": "alice", "params": ["#nonexistent"], "body": "No such channel" }
]
}

21
schema/numerics/433.json Normal file
View File

@@ -0,0 +1,21 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/numerics/433.json",
"title": "433 ERR_NICKNAMEINUSE",
"description": "Nickname is already in use. RFC 1459 §6.1.",
"$ref": "../message.json",
"properties": {
"command": { "const": "433" },
"to": { "type": "string" },
"params": {
"type": "array",
"items": { "type": "string" },
"description": "[requested_nick]."
},
"body": { "const": "Nickname is already in use" }
},
"required": ["command", "to", "params"],
"examples": [
{ "command": "433", "to": "*", "params": ["alice"], "body": "Nickname is already in use" }
]
}

18
schema/numerics/442.json Normal file
View File

@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/numerics/442.json",
"title": "442 ERR_NOTONCHANNEL",
"description": "You're not on that channel. RFC 1459 §6.1.",
"$ref": "../message.json",
"properties": {
"command": { "const": "442" },
"to": { "type": "string" },
"params": {
"type": "array",
"items": { "type": "string" },
"description": "[channel]."
},
"body": { "const": "You're not on that channel" }
},
"required": ["command", "to", "params"]
}

18
schema/numerics/482.json Normal file
View File

@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/numerics/482.json",
"title": "482 ERR_CHANOPRIVSNEEDED",
"description": "You're not channel operator. RFC 1459 §6.1.",
"$ref": "../message.json",
"properties": {
"command": { "const": "482" },
"to": { "type": "string" },
"params": {
"type": "array",
"items": { "type": "string" },
"description": "[channel]."
},
"body": { "const": "You're not channel operator" }
},
"required": ["command", "to", "params"]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/001.schema.json",
"title": "001 RPL_WELCOME (S2C)",
"description": "Welcome message after registration",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "001"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Response lines"
}
},
"required": [
"command",
"to",
"body"
]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/002.schema.json",
"title": "002 RPL_YOURHOST (S2C)",
"description": "Server host information",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "002"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Response lines"
}
},
"required": [
"command",
"to",
"body"
]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/322.schema.json",
"title": "322 RPL_LIST (S2C)",
"description": "Channel list entry",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "322"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Response lines"
}
},
"required": [
"command",
"to",
"body"
]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/353.schema.json",
"title": "353 RPL_NAMREPLY (S2C)",
"description": "Names list for a channel",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "353"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Response lines"
}
},
"required": [
"command",
"to",
"body"
]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/366.schema.json",
"title": "366 RPL_ENDOFNAMES (S2C)",
"description": "End of names list",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "366"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Response lines"
}
},
"required": [
"command",
"to",
"body"
]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/372.schema.json",
"title": "372 RPL_MOTD (S2C)",
"description": "Message of the day line",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "372"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Response lines"
}
},
"required": [
"command",
"to",
"body"
]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/375.schema.json",
"title": "375 RPL_MOTDSTART (S2C)",
"description": "Start of MOTD",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "375"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Response lines"
}
},
"required": [
"command",
"to",
"body"
]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/376.schema.json",
"title": "376 RPL_ENDOFMOTD (S2C)",
"description": "End of MOTD",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "376"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Response lines"
}
},
"required": [
"command",
"to",
"body"
]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/401.schema.json",
"title": "401 ERR_NOSUCHNICK (S2C)",
"description": "No such nick or channel",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "401"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Response lines"
}
},
"required": [
"command",
"to",
"body"
]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/403.schema.json",
"title": "403 ERR_NOSUCHCHANNEL (S2C)",
"description": "No such channel",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "403"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Response lines"
}
},
"required": [
"command",
"to",
"body"
]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/433.schema.json",
"title": "433 ERR_NICKNAMEINUSE (S2C)",
"description": "Nickname already in use",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "433"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Response lines"
}
},
"required": [
"command",
"to",
"body"
]
}

View File

@@ -1,23 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/error.schema.json",
"title": "ERROR (S2C)",
"description": "Server error",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "ERROR"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Error lines"
}
},
"required": [
"command",
"body"
]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/join.schema.json",
"title": "JOIN (S2C)",
"description": "User joined a channel",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "JOIN"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Not used"
}
},
"required": [
"command",
"from",
"to"
]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/kick.schema.json",
"title": "KICK (S2C)",
"description": "User kicked from channel",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "KICK"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Kick reason"
}
},
"required": [
"command",
"from",
"to"
]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/mode.schema.json",
"title": "MODE (S2C)",
"description": "Mode change notification",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "MODE"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Mode params"
}
},
"required": [
"command",
"from",
"to"
]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/nick.schema.json",
"title": "NICK (S2C)",
"description": "User changed nick",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "NICK"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Not used"
}
},
"required": [
"command",
"from",
"to"
]
}

View File

@@ -1,23 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/notice.schema.json",
"title": "NOTICE (S2C)",
"description": "Server or user notice",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "NOTICE"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Text lines"
}
},
"required": [
"command",
"body"
]
}

View File

@@ -1,24 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/part.schema.json",
"title": "PART (S2C)",
"description": "User left a channel",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "PART"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Part message"
}
},
"required": [
"command",
"from",
"to"
]
}

View File

@@ -1,22 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/pong.schema.json",
"title": "PONG (S2C)",
"description": "Server keepalive response",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "PONG"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Pong token"
}
},
"required": [
"command"
]
}

View File

@@ -1,25 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/privmsg.schema.json",
"title": "PRIVMSG (S2C)",
"description": "Relayed message from a user",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "PRIVMSG"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Text lines"
}
},
"required": [
"command",
"from",
"to",
"body"
]
}

View File

@@ -1,34 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/pubkey.schema.json",
"title": "PUBKEY (S2C)",
"description": "Relayed public key announcement",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "PUBKEY"
},
"body": {
"type": "object",
"required": [
"alg",
"key"
],
"properties": {
"alg": {
"type": "string",
"description": "Key algorithm (e.g. ed25519)"
},
"key": {
"type": "string",
"description": "Base64-encoded public key"
}
}
}
},
"required": [
"command",
"from",
"body"
]
}

View File

@@ -1,23 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/quit.schema.json",
"title": "QUIT (S2C)",
"description": "User disconnected",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "QUIT"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Quit message"
}
},
"required": [
"command",
"from"
]
}

View File

@@ -1,25 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2c/topic.schema.json",
"title": "TOPIC (S2C)",
"description": "Topic change notification",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "TOPIC"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Topic lines"
}
},
"required": [
"command",
"from",
"to",
"body"
]
}

View File

@@ -1,20 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2s/link.schema.json",
"title": "LINK (S2S)",
"description": "Establish server link",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "LINK"
},
"body": {
"type": "object",
"description": "Link parameters"
}
},
"required": [
"command",
"body"
]
}

View File

@@ -1,22 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2s/ping.schema.json",
"title": "PING (S2S)",
"description": "Server-to-server keepalive",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "PING"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ping token"
}
},
"required": [
"command"
]
}

View File

@@ -1,22 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2s/pong.schema.json",
"title": "PONG (S2S)",
"description": "Server-to-server keepalive response",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "PONG"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Pong token"
}
},
"required": [
"command"
]
}

View File

@@ -1,20 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2s/relay.schema.json",
"title": "RELAY (S2S)",
"description": "Relay message to linked server",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "RELAY"
},
"body": {
"type": "object",
"description": "Wrapped message"
}
},
"required": [
"command",
"body"
]
}

View File

@@ -1,20 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2s/sync.schema.json",
"title": "SYNC (S2S)",
"description": "Synchronize state between servers",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "SYNC"
},
"body": {
"type": "object",
"description": "State data"
}
},
"required": [
"command",
"body"
]
}

View File

@@ -1,22 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/s2s/unlink.schema.json",
"title": "UNLINK (S2S)",
"description": "Tear down server link",
"$ref": "../message.schema.json",
"properties": {
"command": {
"const": "UNLINK"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Unlink reason"
}
},
"required": [
"command"
]
}