v2.4.0 Security and availability fixes for the AI proxy and server-side rendering, plus the beneficiary lines in the editor (MRs !168, !170, !171, !172, !173, !174, !175). - Security: the AI proxy took the upstream endpoint from the request body and passed it to fetch unchecked, so anything the server could reach a caller could reach through it, with the response returned verbatim. Endpoints now go through a guard that allows https only, refuses addresses that are not publicly routable, and rejects redirects, checking the address inside the connection's DNS lookup so it covers the one actually connected to. The routes also require the same CSRF token the rest of the API does, which stops the proxy being usable as an open relay towards any public host. node-fetch becomes a direct dependency at 2.7.0: it was present only transitively at 1.7.3, which leaks the Authorization header on a cross-host redirect. - The AI proxy had never worked in a container. fetch was used but never imported, and the image runs Node 16, which has no global fetch, so every call answered 500. Fixing that alone would have armed the hole above, so both land together. - Rate limiting: rateLimitReq ignored its own limit argument, keyed on a header the client writes, and never expired its entries. It now takes a per-minute budget counted per route over a sliding window, identifies the visitor by CF-Connecting-IP where the deployment declares it is behind Cloudflare, and evicts the oldest entries at its ceiling rather than turning new callers away. - Availability: a failing remote document no longer kills a worker. The pinned posts, dapps and nsfw lists are fetched from raw URLs, and a redirect, an HTML error page or a rate-limited RPC each produced an uncaught exception or a promise that never settled. Fetching is now one helper that always settles, entries that cannot be read are skipped rather than failing the batch, each loader keeps its last good value, and an unhandled rejection is logged instead of ending the worker. - The AI model picker asks the provider which models the key may use instead of offering a list compiled into the client, which had gone stale to the point where every option failed. The model can always be typed. Upstream failures answer 400 with the provider's message rather than a status a CDN replaces with its own error page. - The AI chat now streams: the event stream was being gzipped, which held the answer back until it was complete. Reasoning models no longer print their chain of thought as the answer, the settings screen and the chat share one configuration store, and code spans are legible in the dark theme. - Logging out clears the assistant. Its conversation and the provider API key live in the browser, so both survived a logout and the next person at that machine could read the chat and spend the key. Expanding the chat window no longer hides its own toolbar behind the site header. - Editor: the beneficiary lines print a total when more than one automatic share applies and name the origin of each, instead of showing two identical lines that contradicted the counter. Translated into the remaining locales.