Replace the process-wide registration counter used without Redis with a
shared per-key fixed-window limiter, so one requester can no longer
exhaust the registration budget for every other client on the process.
The in-memory fallback is bounded (expired windows are swept, then the
oldest keys are shed) so memory stays constant under key churn.
Also fixes checkMcpRateLimit inverting redis.rateLimit's result: it
resolves true once the counter reaches its threshold, so with Redis the
MCP endpoint was denying requests below the limit and allowing those
above it. Both call sites now share the same limiter and semantics.
TWO_FACTOR_ENCRYPTION_KEY has no default, so instances upgrading to 3.3
without setting it hit an unhandled throw from encryptSecret: the setup
dialog only reported "Something went wrong", and where 2FA was required
the blocking dialog made the whole app unusable. The status endpoint now
reports 2FA as not required while the key is missing or invalid, and
setup initiation returns 503 with a two-factor-error-not-configured code
that the setup dialog renders as a readable message. Also documents the
variable in the README and the example compose file.
Closes#4443
Replaced hardcoded `event_type` values with named constants from
`EVENT_TYPE`, as in #3614. Added the `BOUNCE_THRESHOLD` constant and
documented it in `docker-compose.yml`.
The billions branch of formatLongNumber divided by 1000000 while
appending the 'b' suffix, so values >= 1e9 rendered about 1000x too
large (1000000000 became '1000.0b' instead of '1.0b'). Divide by
1000000000 to match the 'b' unit, consistent with the sibling
formatLongCurrency. Add regression tests for the billions branch.
When SASL credentials are present, getClient() connected with
`rejectUnauthorized: false`, so the SASL password was sent over a TLS
connection whose broker certificate was never verified — a MITM could
present any certificate and capture the credentials.
Verify by default; set KAFKA_SSL_ALLOW_UNAUTHORIZED=true to opt out for
self-signed brokers.
The Content-Security-Policy is defined in next.config.ts headers(), which Next
serializes at build time, so ALLOWED_FRAME_URLS only takes effect when it is set
before `next build`. The published Docker images are built without it, so
frame-ancestors cannot be configured at runtime and iframe embedding requires a
custom image (#3793, #2407).
Move the policy into a shared getContentSecurityPolicy() and set the header from
the Docker proxy (docker/proxy.ts) on every response, where runtime env such as
CORS_MAX_AGE and DISABLE_LOGIN is already handled. next.config.ts skips the
build-time CSP when the proxy is present, so exactly one header is sent.
Non-Docker builds are unchanged: the build-time CSP still applies and
ALLOWED_FRAME_URLS keeps working as a build-time variable there.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>