3.2.0
3.2 adds first-class reverse-proxy / ingress support — X-Forwarded-Prefix and X-Ingress-Path are now honoured under trustProxy, so Etherpad can live under a subpath (Traefik, Nginx, Kubernetes Ingress) without breaking the PWA manifest, social-meta URLs, or any of the bootstrap asset links. The admin settings page learns to show resolved runtime values next to ${VAR:default} placeholders, the v3.1.0 admin pad-list filter chips now apply server-side (so "show empty pads" no longer returns 0–12 of hundreds), and the v3.1.0 redesigned outdated-version gritter actually fires in production now (the session-based author lookup it shipped with always returned null for pad visitors).
Notable enhancements
- HTTP — accept
X-Forwarded-PrefixandX-Ingress-PathundertrustProxy(#7802 / #7806). WithtrustProxy: true, Etherpad now honoursX-Forwarded-Prefix(de-facto Traefik / Spring) andX-Ingress-Path(Kubernetes Ingress) in addition to the prefix it already inferred from the request path. The sharedsanitizeProxyPathhelper added in 3.1.0 (defence-in-depth:[A-Za-z0-9_./-]only,//+collapsed,..traversal rejected) is extended to the new headers and applied consistently across/manifest.json,socialMetaog:url/og:image, and theindex.html/pad.html/timeslider.html/export_html.htmltemplates (manifest links, jslicense links, reconnect URLs). A pre-existing..segment-count miscalculation inpad.html/timeslider.htmlthat broke the manifest link when served from a deep subpath is also fixed in passing. New end-to-end suite covers the prefix-applied / prefix-ignored matrix undertrustProxy=true|falsefor both header names.settings.json.templatedocuments the new headers alongside the existingtrustProxynotes. - Admin settings — resolved runtime values surface on env-pill chips (#7803 / #7807). The
/admin/settingssocket payload now carries a newresolvedfield alongside the existing raw-fileresultsblob, carrying the actual in-memory settings module run through a new redactor (AdminSettingsRedact) that replaces known-sensitive paths (users.*.password,dbSettings.password,sso.clients[*].client_secret,sessionKey, …) with[REDACTED]. The admin SPA'sEnvPillrenders a→ active valuechip when the path is resolved, or→ ••••••with a redacted tooltip when the server returned the sentinel — soport: ${PORT:9001}now shows→ 9001(or whatever the live value is) instead of silently falling back to the template default. Old admin SPAs that don't readresolvedcontinue to work; the save round-trip is unchanged so${VAR:default}literals are still preserved verbatim on disk. The admin test script glob picks up.test.tsxalongside.test.tsso the newEnvPillandresolveByPathtests run undertsx --test.
Notable fixes
- Admin pads — filter chip now applies server-side, before pagination (#7798). The 3.1.0 admin pad-list filter chips (
active/recent/empty/stale) ran on the client after the 12-row page slice had already arrived. On a deployment with hundreds of pads, clicking "empty pads" on page 1 only matched the 0–12 empties that happened to land in the current page, with the pagination footer reporting nonsense totals (reported on a 3.1.0 deployment). The filter is now part of thepadLoadsocket query — pattern filter on names runs first (cheap), metadata hydration for the matching pad universe is gated on a non-allfilter or a non-padNamesort and runs under a 16-way concurrency cap (was unboundedPromise.all, which fanned out to thousands of in-flightpadManager.getPad()reads on busy deployments), then the filter chip, then sort + slice.totalreflects the filtered universe so the footer makes sense. Older admin clients that don't sendfilterkeep working — the server defaults toall. Theif/else ifladder that duplicated the hydrate-and-sort loop persortByis folded into one pipeline with a single comparator switch. - Pad outdated notice — author now resolved from token cookie, not session (Qodo #7804 / #7805). The 3.1.0 redesigned outdated-version gritter never fired in production.
resolveRequestAuthor()looked for anauthorIDonreq.session.user, which Etherpad does not populate for pad visitors (express-session only carries the admin-login user), socomputeOutdated()always returned EMPTY. The lookup now mirrors how the socket.io handshake resolves pad-visitor identity — read the HttpOnlytoken(or<prefix>token) cookie and callauthorManager.getAuthorId(token, user)via a dynamic import (same circular-init guard pattern the file already uses forPadManager). The admin OpenAPI document gains adescriptionnote clarifying that/api/version-statusis a public pad-side endpoint that lives in the admin doc only because it shares the same internal route registration. - Localisation — silence spurious "could not translate element content" warning (#7797).
<select data-l10n-id="…">with<option>element children — the pattern used byep_headings2,ep_align,ep_font_size,ep_font_family, … — used to drop into the textContent branch ofhtml10n.translateNode, hunt for a text-node child to overwrite, find none, and emitUnexpected error: could not translate element content for key …on every pad load. TheSELECT/INPUT/TEXTAREAaria-label fallback already lived inside the same else-branch after the warning, so the accessible name landed correctly but the noisy console line still fired. Form-control elements now short-circuit into the aria-label path before the text-node hunt — aria-label is the only sensible localization target for these elements (a<select>'s text is its<option>labels, not its own name). Closes the console warning reported on Etherpad 3.1.0.
Internal / contributor-facing
- CI — swap archived
ep_readonly_guestforep_guestin the plugin matrix (#7795 / #7808).ep_readonly_guestis archived (read-only on GitHub) and itsauthenticatehook unconditionally swappedreq.session.userwith a read-only guest, even when the request carried an HTTP Authorization header. That silently demoted admin login attempts and stalled theanonymizeAuthorSockettests for 14 min/run on every with-plugins CI matrix. The pre-fix theory from 3.1.0 (#7796) blamedep_hash_auth.handleMessage; that was a red herring —handleMessageonly fires on the/padnamespace, never on/settings.ep_guestis the maintained successor (same authors, same purpose); 1.0.72 on npm already defers to basic auth / admin paths. Swapping the matrix unblocks theanonymizeAuthorSocketsuite on Linux, Windows, and the upgrade-from-latest-release workflow. The runtime probe added in #7796 stays — it still catches any other authenticate-hook plugin that rejects the test's plain-text credentials (e.g. a future hashed-only plugin). - Tests — admin
saveSettingsround-trip + cross-restart persistence (#7819 / #7820 / #7821). The adminsaveSettingssocket had zero direct backend coverage and the existing e2e "restart works" test only checked that the page renders after a restart, neither of which catches a deployment that resetssettings.jsonon restart, nor the user-visible workflow that triggered #7819 (add a top-level plugin block via Raw, save, watch it disappear). Three new backend specs (adminSettingsSave.ts) verify byte-for-byte write, top-level-block augmentation round-tripping through the nextload, and/* */comments surviving the write path. A new e2e spec mirrors the #7819 user workflow — open Raw, prepend anep_oauth-shaped top-level block, save,restartEtherpad(), re-login, confirm the block is still in Raw and surfaces as its own Form-view section (Ep oauthfromhumanize()). A separatedocker.ymljob (adminSettings_7819.ts) authenticates viaPOST /admin-auth/(always-requireAdmin, regardless ofsettings.requireAuthentication), saves a hand-built minimal-but-viable settings document containing a marker block,docker exec test greps for it,docker restarts the container, waits for the health probe, and re-greps. Both checks must pass. - Bug report template now asks contributors whether the abstraction in their proposed fix matches the rest of the codebase, to head off premature-generalisation fixes earlier in review.
Dependencies
ueberdb26.0.3 → 6.1.2 (two patch releases of cleanup on top of the 6.1.0findKeysPagedAPI that the 3.1.0 sessionstorage OOM fix relies on).semver7.8.0 → 7.8.1,lru-cache11.3.6 → 11.5.0,@elastic/elasticsearch9.4.0 → 9.4.1,pg8.20.0 → 8.21.0,openapi-backend5.16.1 → 5.17.0,tsx4.22.0 → 4.22.3,@tanstack/react-query5.100.10 → 5.100.11 +@tanstack/react-query-devtools,js-cookie3.0.6 → 3.0.7, plus two dev-dependency group bumps.
Localisation
- Multiple updates from translatewiki.net.