release(v4.8.1): SHADE_DISABLE_RATE_LIMIT env var for single-tenant deploys
Plumbing fix only — both createPrekeyRoutes and createInboxRoutes already accepted disableRateLimit; standalone.ts just didn't read the env. Now SHADE_DISABLE_RATE_LIMIT=1 turns off IP rate-limits on every prekey + inbox route, with a WARN log on startup so operators see it. Single-tenant deployments only — multi-tenant relays must leave it unset. Documented in docs/DEPLOYMENT.md. Reported by Prism: ~6 pair attempts/hour from a single dev IP + the sidecar's register call tripped the 5/hour REGISTER_LIMIT every dev iteration. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -102,6 +102,48 @@ describe('Rate limiting integration with routes', () => {
|
||||
expect(results.filter((s) => s === 429).length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
// V4.8.1 — `SHADE_DISABLE_RATE_LIMIT=1` in standalone.ts is plumbed
|
||||
// through to `createPrekeyServer({ disableRateLimit })`. This test
|
||||
// covers the "what happens when the flag is true" path; the env-var
|
||||
// → option conversion in standalone.ts is a one-liner verified by
|
||||
// inspection.
|
||||
test('register endpoint allows >5/hour from a single IP when disableRateLimit is set', async () => {
|
||||
const app = createPrekeyServer({
|
||||
crypto,
|
||||
store: new MemoryPrekeyStore(),
|
||||
disableRateLimit: true,
|
||||
});
|
||||
|
||||
async function doRegister(addressSuffix: number) {
|
||||
const identity = await generateIdentityKeyPair(crypto);
|
||||
const body: any = {
|
||||
address: `user${addressSuffix}`,
|
||||
identitySigningKey: Buffer.from(identity.signingPublicKey).toString('base64'),
|
||||
identityDHKey: Buffer.from(identity.dhPublicKey).toString('base64'),
|
||||
signedPreKey: {
|
||||
keyId: 1,
|
||||
publicKey: Buffer.from(crypto.randomBytes(32)).toString('base64'),
|
||||
signature: Buffer.from(crypto.randomBytes(64)).toString('base64'),
|
||||
},
|
||||
};
|
||||
const signed = await signPayload(crypto, identity.signingPrivateKey, body);
|
||||
return app.request('/v1/keys/register', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'x-forwarded-for': '203.0.113.1' },
|
||||
body: JSON.stringify(signed),
|
||||
});
|
||||
}
|
||||
|
||||
const results: number[] = [];
|
||||
for (let i = 0; i < 12; i++) {
|
||||
const res = await doRegister(i);
|
||||
results.push(res.status);
|
||||
}
|
||||
// No 429 anywhere — the limit is OFF.
|
||||
expect(results.filter((s) => s === 429).length).toBe(0);
|
||||
expect(results.filter((s) => s === 200).length).toBe(12);
|
||||
});
|
||||
|
||||
test('rate limit returns Retry-After header', async () => {
|
||||
const app = createPrekeyServer({ crypto, store: new MemoryPrekeyStore() });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user