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)

OP OpenVibers committed 27 days ago · 9cc9b3fa8f · 2 files, +21 −1

Changes

ddiff --git a/server/index.js b/server/index.js
iindex 592b1b66..688977cf 100644
---- a/server/index.js
++++ b/server/index.js
@@@ -298,7 +298,11 @@ app.use(require('./public-cors').gate(require('./public-cors').restrictedCors(co
app.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 ─────────────────────────────────────────────────
ddiff --git a/test/api-rate-limit.test.js b/test/api-rate-limit.test.js
nnew file mode 100644
iindex 00000000..ec8c3814
---- /dev/null
++++ b/test/api-rate-limit.test.js
@@@ -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');