···17561756 did TEXT PRIMARY KEY
17571757 );
1758175817591759+ CREATE TABLE IF NOT EXISTS inbox_rate_limit (
17601760+ fromDid TEXT NOT NULL,
17611761+ sentAt TEXT NOT NULL
17621762+ );
17631763+17591764 CREATE INDEX IF NOT EXISTS idx_inbox_messages_from ON inbox_messages(fromDid);
17651765+ CREATE INDEX IF NOT EXISTS idx_inbox_rate_limit_from ON inbox_rate_limit(fromDid, sentAt);
17601766 `);
17611767 }
17621768···33573363 const now = new Date().toISOString();
3358336433593365 if (accepted.length > 0) {
33603360- // Deliver directly to inbox
33663366+ // Rate limit: max 10 messages per minute per sender
33673367+ const cutoff = new Date(Date.now() - 60 * 1000).toISOString();
33683368+33693369+ // Clean up old rate limit entries
33703370+ this.sql.exec('DELETE FROM inbox_rate_limit WHERE sentAt < ?', cutoff);
33713371+33723372+ // Count recent messages from this sender
33733373+ const recentCount = this.sql
33743374+ .exec('SELECT COUNT(*) as cnt FROM inbox_rate_limit WHERE fromDid = ? AND sentAt >= ?', senderDid, cutoff)
33753375+ .toArray()[0].cnt;
33763376+33773377+ if (recentCount >= 10) {
33783378+ return Response.json({ status: 'rate_limited' });
33793379+ }
33803380+33813381+ // Record this message for rate limiting
33823382+ this.sql.exec('INSERT INTO inbox_rate_limit (fromDid, sentAt) VALUES (?, ?)', senderDid, now);
33833383+33843384+ // Deliver to inbox
33613385 this.sql.exec(
33623386 'INSERT INTO inbox_messages (fromDid, text, createdAt) VALUES (?, ?, ?)',
33633387 senderDid,