diff options
| author | Adam Soltys <[email protected]> | 2026-03-18 11:39:05 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-03-18 11:39:05 -0700 |
| commit | a8977f8c5c84b0c37c4fb36f0a0be5bf6f492f3d (patch) | |
| tree | 49a3a13119ef23de9b1b9401700f9415514ea00d | |
| parent | e3ed16def9bee6947fd5d929b90f246a8e5ec2dc (diff) | |
| download | coinos-server-a8977f8c5c84b0c37c4fb36f0a0be5bf6f492f3d.tar.gz coinos-server-a8977f8c5c84b0c37c4fb36f0a0be5bf6f492f3d.zip | |
stop republishing external events and add NWC rate limiting
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
| -rw-r--r-- | lib/nostr.ts | 2 | ||||
| -rw-r--r-- | lib/nwc.ts | 15 | ||||
| -rw-r--r-- | routes/users.ts | 3 |
3 files changed, 15 insertions, 5 deletions
diff --git a/lib/nostr.ts b/lib/nostr.ts index a2aee59b..1e911f39 100644 --- a/lib/nostr.ts +++ b/lib/nostr.ts @@ -9,7 +9,6 @@ import type { Event } from "nostr-tools"; import { Relay } from "nostr-tools/relay"; export const EX = 60 * 60 * 24; -const coinos = await Relay.connect(config.nostr); export const serverSecret = bytesToHex(nip19.decode(config.nostrKey).data as Uint8Array); @@ -222,7 +221,6 @@ export const q = async (f) => { r.subscribe([f], { onevent(e) { found.push(e); - coinos.publish(e).catch(() => {}); }, oneose() { r.close(); @@ -37,6 +37,11 @@ const nwcEventMaxAgeSeconds = 5 * 60; const handledKey = "handled:nwc"; const handledMaxSize = 200000; +// Per-pubkey rate limiting for NWC requests +const nwcRateLimit = 5; // max requests per minute per pubkey +const nwcRateWindow = 60 * 1000; // 1 minute in ms +const nwcRequestTimes: Map<string, number[]> = new Map(); + export default () => { let r: any; @@ -45,7 +50,7 @@ export default () => { r.on("open", async (_) => { l("nwc connected to strfry"); - r.subscribe("nwc", { kinds: [23194], "#p": [serverPubkey, serverPubkey2] }); + r.subscribe("nwc", { kinds: [23194], "#p": [serverPubkey, serverPubkey2], since: Math.floor(Date.now() / 1000) - nwcEventMaxAgeSeconds }); const info = await finalizeEvent( { created_at: Math.floor(Date.now() / 1000), @@ -89,6 +94,14 @@ export default () => { if (ev.created_at && now - ev.created_at > nwcEventMaxAgeSeconds) return; if (await db.zScore(handledKey, ev.id)) return; + // Per-pubkey rate limiting + const times = nwcRequestTimes.get(ev.pubkey) || []; + const cutoff = Date.now() - nwcRateWindow; + const recent = times.filter((t) => t > cutoff); + if (recent.length >= nwcRateLimit) return; + recent.push(Date.now()); + nwcRequestTimes.set(ev.pubkey, recent); + db.zAdd(handledKey, { score: now, value: ev.id }); db.zRemRangeByScore(handledKey, 0, now - nwcEventMaxAgeSeconds); const size = await db.zCard(handledKey); diff --git a/routes/users.ts b/routes/users.ts index 999a6d05..00083e38 100644 --- a/routes/users.ts +++ b/routes/users.ts @@ -158,8 +158,7 @@ export default { const user = await getNostrUser(key); return c.json(pick(user, fields)); } catch (e) { - err("problem getting user", key, e.message); - return c.json(e.message, 500); + return c.json("User not found", 404); } }, |
