feat: add IRC-style message protocol JSON schemas (draft 2020-12)

Add JSON Schema definitions for all message types:
- Base message envelope (message.schema.json)
- C2S: PRIVMSG, NOTICE, JOIN, PART, QUIT, NICK, MODE, TOPIC, KICK, PING, PUBKEY
- S2C: named commands + numeric reply codes (001, 002, 322, 353, 366, 372, 375, 376, 401, 403, 433)
- S2S: RELAY, LINK, UNLINK, SYNC, PING, PONG
- Schema index (schema/README.md)

All messages use IRC command names and numeric codes from RFC 1459/2812.
Bodies are always objects or arrays (never raw strings) to support
deterministic canonicalization (RFC 8785 JCS) and message signing.
This commit is contained in:
clawbot
2026-02-10 10:26:32 -08:00
parent 4645be5f20
commit 909da3cc99
72 changed files with 1166 additions and 770 deletions

View File

@@ -1,17 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/join.json",
"title": "C2S Join",
"description": "Join a channel. Submitted via POST /api/v1/channels/join.",
"type": "object",
"properties": {
"channel": {
"type": "string",
"description": "Channel name (# prefix optional, server will add it).",
"pattern": "^#?[a-zA-Z0-9_-]+$",
"examples": ["#general", "dev"]
}
},
"required": ["channel"],
"additionalProperties": false
}

View File

@@ -0,0 +1,23 @@
{
"$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

@@ -0,0 +1,23 @@
{
"$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,26 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/mode.json",
"title": "C2S Mode",
"description": "Set channel or user mode flags.",
"type": "object",
"properties": {
"channel": {
"type": "string",
"description": "Target channel.",
"pattern": "^#[a-zA-Z0-9_-]+$"
},
"mode": {
"type": "string",
"description": "Mode string (e.g. +o, -m, +v).",
"pattern": "^[+-][a-zA-Z]+$",
"examples": ["+o", "-m", "+v", "+i"]
},
"target": {
"type": "string",
"description": "Target nick for user modes (e.g. +o alice). Omit for channel modes."
}
},
"required": ["channel", "mode"],
"additionalProperties": false
}

View File

@@ -0,0 +1,23 @@
{
"$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,18 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/nick.json",
"title": "C2S Nick",
"description": "Change the user's nickname.",
"type": "object",
"properties": {
"nick": {
"type": "string",
"description": "Desired new nickname.",
"minLength": 1,
"maxLength": 32,
"pattern": "^[a-zA-Z][a-zA-Z0-9_-]*$"
}
},
"required": ["nick"],
"additionalProperties": false
}

View File

@@ -0,0 +1,23 @@
{
"$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

@@ -0,0 +1,24 @@
{
"$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,22 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/part.json",
"title": "C2S Part",
"description": "Leave a channel. Submitted via DELETE /api/v1/channels/{name}.",
"type": "object",
"properties": {
"channel": {
"type": "string",
"description": "Channel name to leave.",
"pattern": "^#[a-zA-Z0-9_-]+$",
"examples": ["#general"]
},
"reason": {
"type": "string",
"description": "Optional part reason message.",
"maxLength": 256
}
},
"required": ["channel"],
"additionalProperties": false
}

View File

@@ -0,0 +1,23 @@
{
"$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,14 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/ping.json",
"title": "C2S Ping",
"description": "Client keepalive. Server responds with a pong.",
"type": "object",
"properties": {
"token": {
"type": "string",
"description": "Optional opaque token echoed back in the pong response."
}
},
"additionalProperties": false
}

View File

@@ -0,0 +1,22 @@
{
"$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

@@ -0,0 +1,24 @@
{
"$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

@@ -0,0 +1,33 @@
{
"$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

@@ -0,0 +1,22 @@
{
"$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,22 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/send.json",
"title": "C2S Send",
"description": "Send a message to a channel or user. Submitted via POST /api/v1/messages.",
"type": "object",
"properties": {
"to": {
"type": "string",
"description": "Target: channel name (prefixed with #) or nick for DM.",
"examples": ["#general", "alice"]
},
"content": {
"type": "string",
"description": "Message body (UTF-8 text).",
"minLength": 1,
"maxLength": 4096
}
},
"required": ["to", "content"],
"additionalProperties": false
}

View File

@@ -1,21 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://git.eeqj.de/sneak/chat/schema/c2s/topic.json",
"title": "C2S Topic",
"description": "Set a channel's topic.",
"type": "object",
"properties": {
"channel": {
"type": "string",
"description": "Target channel.",
"pattern": "^#[a-zA-Z0-9_-]+$"
},
"topic": {
"type": "string",
"description": "New topic text. Empty string clears the topic.",
"maxLength": 512
}
},
"required": ["channel", "topic"],
"additionalProperties": false
}

View File

@@ -0,0 +1,23 @@
{
"$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"
]
}