All checks were successful
check / check (push) Successful in 2m18s
Add per-channel hashcash requirement via MODE +H <bits>. When set, PRIVMSG to the channel must include a valid hashcash stamp in the meta.hashcash field bound to the channel name and message body hash. Server validates stamp format, difficulty, date freshness, channel binding, body hash binding, and proof-of-work. Spent stamps are persisted to SQLite with 1-year TTL for replay prevention. Stamp format: 1:bits:YYMMDD:channel:bodyhash:counter Changes: - Schema: add hashcash_bits column to channels, spent_hashcash table - DB: queries for get/set channel hashcash bits, spent token CRUD - Hashcash: ChannelValidator, BodyHash, StampHash, MintChannelStamp - Handlers: validate hashcash on PRIVMSG, MODE +H/-H support - Pass meta through fanOut chain to store in messages - Prune spent hashcash tokens in cleanup loop (1-year TTL) - Client: MintChannelHashcash helper for CLI - Tests: 12 new channel_test.go + 10 new api_test.go integration tests - README: document +H mode, stamp format, and usage
82 lines
3.1 KiB
SQL
82 lines
3.1 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,
|
|
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,
|
|
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,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- 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,
|
|
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);
|