check / check (push) Waiting to run
USERHOST, VERSION, ADMIN, INFO, TIME, KILL and WALLOPS work on the HTTP API and on the IRC listener. The USERHOST reply, the VERSION, ADMIN and INFO text, KILL and WALLOPS are each one service function that both transports call. User mode +w selects who receives WALLOPS. MODE on your own nick queries or changes your user modes through one parser on both transports: the whole mode string is checked before anything is stored, and both flags are stored in one transaction. KILL closes the victim's IRC connection after sending it KILL and ERROR, without the operator waiting on it. The sessions table gains is_wallops in 001_initial.sql, as the pre-1.0 rule in REPO_POLICIES.md asks. Model: opus-5-5 Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
120 lines
4.7 KiB
SQL
120 lines
4.7 KiB
SQL
-- Chat server schema (pre-1.0 consolidated)
|
|
PRAGMA foreign_keys = ON;
|
|
|
|
-- Sessions: each session is a user identity (nick + optional password + signing key)
|
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
uuid TEXT NOT NULL UNIQUE,
|
|
nick TEXT NOT NULL UNIQUE,
|
|
username TEXT NOT NULL DEFAULT '',
|
|
hostname TEXT NOT NULL DEFAULT '',
|
|
ip TEXT NOT NULL DEFAULT '',
|
|
is_oper INTEGER NOT NULL DEFAULT 0,
|
|
is_wallops INTEGER NOT NULL DEFAULT 0,
|
|
password_hash TEXT NOT NULL DEFAULT '',
|
|
signing_key TEXT NOT NULL DEFAULT '',
|
|
away_message TEXT NOT NULL DEFAULT '',
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
last_seen DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_uuid ON sessions(uuid);
|
|
|
|
-- Clients: each session can have multiple connected clients
|
|
CREATE TABLE IF NOT EXISTS clients (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
uuid TEXT NOT NULL UNIQUE,
|
|
session_id INTEGER NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
token TEXT NOT NULL UNIQUE,
|
|
ip TEXT NOT NULL DEFAULT '',
|
|
hostname TEXT NOT NULL DEFAULT '',
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
last_seen DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_clients_token ON clients(token);
|
|
CREATE INDEX IF NOT EXISTS idx_clients_session ON clients(session_id);
|
|
|
|
-- Channels
|
|
CREATE TABLE IF NOT EXISTS channels (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL UNIQUE,
|
|
topic TEXT NOT NULL DEFAULT '',
|
|
topic_set_by TEXT NOT NULL DEFAULT '',
|
|
topic_set_at DATETIME,
|
|
hashcash_bits INTEGER NOT NULL DEFAULT 0,
|
|
is_moderated INTEGER NOT NULL DEFAULT 0,
|
|
is_topic_locked INTEGER NOT NULL DEFAULT 1,
|
|
is_invite_only INTEGER NOT NULL DEFAULT 0,
|
|
is_secret INTEGER NOT NULL DEFAULT 0,
|
|
is_no_external INTEGER NOT NULL DEFAULT 1,
|
|
channel_key TEXT NOT NULL DEFAULT '',
|
|
user_limit INTEGER NOT NULL DEFAULT 0,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Channel bans
|
|
CREATE TABLE IF NOT EXISTS channel_bans (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
channel_id INTEGER NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
|
|
mask TEXT NOT NULL,
|
|
set_by TEXT NOT NULL,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(channel_id, mask)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_bans_channel ON channel_bans(channel_id);
|
|
|
|
-- Channel invites (in-memory would be simpler but DB survives restarts)
|
|
CREATE TABLE IF NOT EXISTS channel_invites (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
channel_id INTEGER NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
|
|
session_id INTEGER NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
invited_by TEXT NOT NULL DEFAULT '',
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(channel_id, session_id)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_channel_invites_channel ON channel_invites(channel_id);
|
|
|
|
-- Channel members
|
|
CREATE TABLE IF NOT EXISTS channel_members (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
channel_id INTEGER NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
|
|
session_id INTEGER NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
is_operator INTEGER NOT NULL DEFAULT 0,
|
|
is_voiced INTEGER NOT NULL DEFAULT 0,
|
|
joined_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(channel_id, session_id)
|
|
);
|
|
|
|
-- Messages: IRC envelope format
|
|
CREATE TABLE IF NOT EXISTS messages (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
uuid TEXT NOT NULL UNIQUE,
|
|
command TEXT NOT NULL DEFAULT 'PRIVMSG',
|
|
msg_from TEXT NOT NULL DEFAULT '',
|
|
msg_to TEXT NOT NULL DEFAULT '',
|
|
params TEXT NOT NULL DEFAULT '[]',
|
|
body TEXT NOT NULL DEFAULT '[]',
|
|
meta TEXT NOT NULL DEFAULT '{}',
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_messages_to_id ON messages(msg_to, id);
|
|
CREATE INDEX IF NOT EXISTS idx_messages_created ON messages(created_at);
|
|
|
|
-- Spent hashcash tokens for replay prevention (1-year TTL)
|
|
CREATE TABLE IF NOT EXISTS spent_hashcash (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
stamp_hash TEXT NOT NULL UNIQUE,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_spent_hashcash_created ON spent_hashcash(created_at);
|
|
|
|
-- Per-client message queues for fan-out delivery
|
|
CREATE TABLE IF NOT EXISTS client_queues (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
client_id INTEGER NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
|
|
message_id INTEGER NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(client_id, message_id)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_client_queues_client ON client_queues(client_id, id);
|