A zero-dependency AT Protocol Personal Data Server written in JavaScript

add rate limiting to inbox (10/min per sender)

Changed files
+25 -1
src
+25 -1
src/pds.js
··· 1756 1756 did TEXT PRIMARY KEY 1757 1757 ); 1758 1758 1759 + CREATE TABLE IF NOT EXISTS inbox_rate_limit ( 1760 + fromDid TEXT NOT NULL, 1761 + sentAt TEXT NOT NULL 1762 + ); 1763 + 1759 1764 CREATE INDEX IF NOT EXISTS idx_inbox_messages_from ON inbox_messages(fromDid); 1765 + CREATE INDEX IF NOT EXISTS idx_inbox_rate_limit_from ON inbox_rate_limit(fromDid, sentAt); 1760 1766 `); 1761 1767 } 1762 1768 ··· 3357 3363 const now = new Date().toISOString(); 3358 3364 3359 3365 if (accepted.length > 0) { 3360 - // Deliver directly to inbox 3366 + // Rate limit: max 10 messages per minute per sender 3367 + const cutoff = new Date(Date.now() - 60 * 1000).toISOString(); 3368 + 3369 + // Clean up old rate limit entries 3370 + this.sql.exec('DELETE FROM inbox_rate_limit WHERE sentAt < ?', cutoff); 3371 + 3372 + // Count recent messages from this sender 3373 + const recentCount = this.sql 3374 + .exec('SELECT COUNT(*) as cnt FROM inbox_rate_limit WHERE fromDid = ? AND sentAt >= ?', senderDid, cutoff) 3375 + .toArray()[0].cnt; 3376 + 3377 + if (recentCount >= 10) { 3378 + return Response.json({ status: 'rate_limited' }); 3379 + } 3380 + 3381 + // Record this message for rate limiting 3382 + this.sql.exec('INSERT INTO inbox_rate_limit (fromDid, sentAt) VALUES (?, ?)', senderDid, now); 3383 + 3384 + // Deliver to inbox 3361 3385 this.sql.exec( 3362 3386 'INSERT INTO inbox_messages (fromDid, text, createdAt) VALUES (?, ?, ?)', 3363 3387 senderDid,