aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-03-18 11:39:05 -0700
committerAdam Soltys <[email protected]>2026-03-18 11:39:05 -0700
commita8977f8c5c84b0c37c4fb36f0a0be5bf6f492f3d (patch)
tree49a3a13119ef23de9b1b9401700f9415514ea00d
parente3ed16def9bee6947fd5d929b90f246a8e5ec2dc (diff)
downloadcoinos-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.ts2
-rw-r--r--lib/nwc.ts15
-rw-r--r--routes/users.ts3
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();
diff --git a/lib/nwc.ts b/lib/nwc.ts
index b0f1021f..0d18f744 100644
--- a/lib/nwc.ts
+++ b/lib/nwc.ts
@@ -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);
}
},