openvibers/openvibe.network Commits
API limit per address 600 a minute, nginx's own rate: at 120 a person clicking through their account pages (about 20 requests each, plus every open tab's navbar) got 429s and "Couldn't load your account" on the fifth page (#276)
Changes
@@@ -298,7 +298,11 @@ app.use(require('./public-cors').gate(require('./public-cors').restrictedCors(coapp.use(corsGuard.denied);// ── Rate Limiting ────────────────────────────────────────────--app.use('/api/', rateLimit({ windowMs: 60_000, max: 120 }));++// 600 a minute per address, nginx's own rate for /api/ (openvibenetwork_api, 10 r/s). One account page asks ~20 things at++// once, and every open OpenVibe tab's navbar (frame, session, unread count) and everyone behind the same home connection++// spend this budget: at 120 the fifth account page within 20 s got 429s and "Couldn't load your account" (2026-10-17).++// Sign-in, account actions, pairing and the other sensitive routes keep their own, stricter limits below.++app.use('/api/', rateLimit({ windowMs: 60_000, max: 600 }));app.use('/api/auth/', rateLimit({ windowMs: 15 * 60_000, max: 30, skipSuccessfulRequests: true }));// ── Database ─────────────────────────────────────────────────nnew file mode 100644@@@ -0,0 +1,16 @@++'use strict';++// The general /api/ limit per address matches nginx's (600 a minute): a person clicking through their account pages++// (each asks ~20 things at once) while other OpenVibe tabs poll their navbar must not be locked out. At 120 the fifth++// account page within 20 s answered 429 (2026-10-17). The sensitive routes keep their stricter limits.++//++// node test/api-rate-limit.test.js++const assert = require('assert');++const fs = require('fs');++const path = require('path');++const src = fs.readFileSync(path.join(__dirname, '../server/index.js'), 'utf8');++assert.ok(src.includes("app.use('/api/', rateLimit({ windowMs: 60_000, max: 600 }));"), 'general API: 600 a minute per address');++assert.ok(src.includes("app.use('/api/auth/', rateLimit({ windowMs: 15 * 60_000, max: 30, skipSuccessfulRequests: true }));"), 'failed sign-ins stay at 30 per 15 minutes');++assert.ok(src.includes("app.use('/api/v1/node-pairing', rateLimit({ windowMs: 60_000, max: 10 })"), 'pairing stays at 10 a minute');++const conf = fs.readFileSync(path.join(__dirname, '../deploy/nginx/openvibe.network.conf'), 'utf8');++assert.ok(/zone=openvibenetwork_api:10m rate=10r\/s/.test(conf), "nginx's /api/ zone is the 10 r/s the app now matches");++console.log('api rate limit: all checks passed');
@@ -298,7 +298,11 @@ app.use(require('./public-cors').gate(require('./public-cors').restrictedCors(co
@@ -298,7 +298,11 @@ app.use(require('./public-cors').gate(require('./public-cors').restrictedCors(co
app.use(corsGuard.denied);
app.use(corsGuard.denied);
// ── Rate Limiting ────────────────────────────────────────────
// ── Rate Limiting ────────────────────────────────────────────
-app.use('/api/', rateLimit({ windowMs: 60_000, max: 120 }));
+// 600 a minute per address, nginx's own rate for /api/ (openvibenetwork_api, 10 r/s). One account page asks ~20 things at
+// once, and every open OpenVibe tab's navbar (frame, session, unread count) and everyone behind the same home connection
+// spend this budget: at 120 the fifth account page within 20 s got 429s and "Couldn't load your account" (2026-10-17).
+// Sign-in, account actions, pairing and the other sensitive routes keep their own, stricter limits below.
+app.use('/api/', rateLimit({ windowMs: 60_000, max: 600 }));
app.use('/api/auth/', rateLimit({ windowMs: 15 * 60_000, max: 30, skipSuccessfulRequests: true }));
app.use('/api/auth/', rateLimit({ windowMs: 15 * 60_000, max: 30, skipSuccessfulRequests: true }));
// ── Database ─────────────────────────────────────────────────
// ── Database ─────────────────────────────────────────────────
new file mode 100644
new file mode 100644
@@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
+'use strict';
+// The general /api/ limit per address matches nginx's (600 a minute): a person clicking through their account pages
+// (each asks ~20 things at once) while other OpenVibe tabs poll their navbar must not be locked out. At 120 the fifth
+// account page within 20 s answered 429 (2026-10-17). The sensitive routes keep their stricter limits.
+//
+// node test/api-rate-limit.test.js
+const assert = require('assert');
+const fs = require('fs');
+const path = require('path');
+const src = fs.readFileSync(path.join(__dirname, '../server/index.js'), 'utf8');
+assert.ok(src.includes("app.use('/api/', rateLimit({ windowMs: 60_000, max: 600 }));"), 'general API: 600 a minute per address');
+assert.ok(src.includes("app.use('/api/auth/', rateLimit({ windowMs: 15 * 60_000, max: 30, skipSuccessfulRequests: true }));"), 'failed sign-ins stay at 30 per 15 minutes');
+assert.ok(src.includes("app.use('/api/v1/node-pairing', rateLimit({ windowMs: 60_000, max: 10 })"), 'pairing stays at 10 a minute');
+const conf = fs.readFileSync(path.join(__dirname, '../deploy/nginx/openvibe.network.conf'), 'utf8');
+assert.ok(/zone=openvibenetwork_api:10m rate=10r\/s/.test(conf), "nginx's /api/ zone is the 10 r/s the app now matches");
+console.log('api rate limit: all checks passed');