* docs(updater): PR 2 (Tier 2 manual-click) implementation plan 20-task TDD plan for shipping the manual-click update flow on top of the Tier 1 (notify) work merged in #7601. Covers UpdateExecutor, RollbackHandler, SessionDrainer, lock + trustedKeys, four admin endpoints (apply / cancel / acknowledge / log), admin UI updates, integration tests against a tmp git repo, and a manual smoke runbook for the spec's "before each tier ships" gate. Plan deliberately scopes signature verification to an opt-in stub (updates.requireSignature: false default) to avoid blocking on a separate release-signing project. Plan: docs/superpowers/plans/2026-05-08-auto-update-pr2-manual-click.md Spec: docs/superpowers/specs/2026-04-25-auto-update-design.md Issue: ether/etherpad#7607 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): extend state + settings for Tier 2 manual-click Adds ExecutionStatus discriminated union, bootCount, and lastResult to UpdateState, plus the preApplyGraceMinutes/drainSeconds/diskSpaceMinMB/ requireSignature/trustedKeysPath knobs that Tier 2's executor needs. loadState backfills the new fields on Tier 1 state files so existing installs keep working. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): PID-based update.lock with stale-pid reaping Single-flight guard for Tier 2's UpdateExecutor. Atomic O_CREAT|O_EXCL acquire; on EEXIST, sends signal 0 to the recorded PID and reaps if dead. Unparseable / partially-written lock files are treated as stale rather than fatal so a half-written lock from a SIGKILL'd parent doesn't lock the install out forever. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): verifyReleaseTag — gpg-via-git stub for Tier 2 preflight Default updates.requireSignature=false: log a warning and return ok with reason=signature-not-required. Set true to make preflight refuse a tag whose signature does not verify under the system keyring (or trustedKeysPath via GNUPGHOME). Etherpad's release process does not yet sign tags consistently; turning the check on by default would break Tier 2 for every admin and forcing a release-signing change is out of scope for this PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): preflight check pipeline for Tier 2 Pure orchestrator over injected probes for install-method, working tree, disk space, pnpm presence, lock state, remote tag existence and signature verification. Cheap-and-definitive checks run first; first failure short-circuits with a typed reason that the route layer will surface in the preflight-failed admin banner. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): rolling update.log helpers (appendLine + tailLines) Direct file-append + size-based rotation rather than a log4js appender — avoids re-configuring log4js on top of the user's existing logconfig. appendLine creates parents, rotates at 10MB (configurable), keeps 5 backups by default. tailLines reads the last N lines for /admin/update/log. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): SessionDrainer + handshake guard Drainer schedules T-60 / -30 / -10 broadcasts and resolves at T=0; isAcceptingConnections() flips off for the duration. PadMessageHandler consults the flag at the start of CLIENT_READY and disconnects new joiners with reason "updateInProgress" — existing sockets are unaffected. Drains shorter than 30s collapse the early timers to fire ASAP rather than queue past the drain end. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): UpdateExecutor — snapshot, fetch/checkout/install/build, exit 75 Pure-DI orchestrator: spawnFn, copyFile, readSha, saveState, exit are all injected so unit tests run the full pipeline without spawning real children or mutating the real install. Streams stdout/stderr to update.log via the now-best-effort appendLine helper (swallows fs errors so the executor itself never breaks on read-only / unwritable log dirs). Failure paths transition to rolling-back and return — the route layer hands off to RollbackHandler which owns the rollback exit, so we don't double-exit and lose tail lines. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): RollbackHandler — health-check timer + crash-loop guard checkPendingVerification arms a 60s timer at boot when state is pending-verification and increments bootCount; bootCount>2 forces an immediate rollback (crash-loop guard). markVerified persists the verified state and stops the timer. performRollback restores the backup lockfile, runs git checkout <fromSha> and pnpm install, lands on rolled-back or rollback-failed (terminal) on sub-step failure, exits 75 either way so the supervisor restart brings the new state up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): wire RollbackHandler into boot + UpdatePolicy honours rollback-failed - expressCreateServer now invokes checkPendingVerification before polling starts so a previous boot's pending-verification either re-arms the health-check timer or, when bootCount has climbed past the crash-loop threshold, forces an immediate rollback. - server.ts calls markBootHealthy after state hits RUNNING so /health-being-up is the implicit happy-path signal that cancels the rollback timer. - /admin/update/status surfaces execution + lastResult + lockHeld so the admin UI can render the right Apply / Cancel / Acknowledge state. - UpdatePolicy gains an `executionStatus` input. While it equals 'rollback-failed', canAuto / canAutonomous are denied (reason: rollback-failed-terminal); manual stays on because clicking Apply IS the intervention the terminal state needs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): apply / cancel / acknowledge / log endpoints Strict admin-only POSTs that drive Tier 2's manual-click flow: - POST /admin/update/apply: acquire lock, persist preflight, run preflight, drain $drainSeconds, executeUpdate (which exits 75 on success), or run performRollback on a failure path (also exits 75). - POST /admin/update/cancel: cancel a pre-execute drain/preflight, write cancelled lastResult, release lock. - POST /admin/update/acknowledge: clear terminal states (preflight-failed, rolled-back, rollback-failed) back to idle. lastResult is preserved so the admin still sees what happened. - GET /admin/update/log: tail var/log/update.log (200 lines) for the in- progress UI. Strict admin auth. Also: - socketio hook exports getIo() so the apply endpoint can broadcast the drain shoutMessage outside the regular hook surface. - ep.json registers updateActions after admin/updateStatus. - 11 mocha integration tests cover auth, policy denial, execution-busy, acknowledge-clears-terminal, log content-type. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): admin UI Apply/Cancel/Acknowledge + live log stream UpdatePage renders the right action set based on execution.status: Apply when idle/verified and policy allows, Cancel during preflight/draining, Acknowledge on terminal preflight-failed / rolled-back / rollback-failed. While the executor is in flight (preflight/draining/executing/rolling-back) the page polls /admin/update/log + /admin/update/status once a second and shows the rolling tail; polling stops automatically when the run terminates. lastResult and policy denial reasons surface localised copy. Buttons disable themselves while a network round-trip is in flight to dodge double-clicks. New i18n keys live under update.page.{apply,cancel, acknowledge,log,execution,policy.*,last_result.*}, update.execution.*, update.banner.terminal.rollback-failed, and update.drain.{t60,t30,t10}. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): pad shoutMessage renders update.drain.* via html10n broadcastShout now sends {messageKey, values, sticky} so the existing pad-side shout pipeline can route through html10n.get(). The renderer gains a values pass-through so update.drain.t60 etc. interpolate {{seconds}}, and gives updater shouts a different gritter title (the banner.title localised string) so users know it's a system event rather than a generic admin message. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): rollback uses git checkout -f + integration suite over tmp git repo RollbackHandler now does git checkout -f <fromSha> BEFORE overlaying the backup lockfile. Without -f, git refuses checkout when there are unstaged modifications to files it would overwrite — exactly the case after a partial executor run that mutated the working tree. With -f the partial mutation is discarded and the working tree returns to fromSha cleanly. The backup-lockfile copy is still done (belt-and-braces) but tolerates ENOENT since checkout already restored the right lockfile. The new integration suite at src/tests/backend/specs/updater-integration.ts exercises the full pipeline against a disposable git repo: happy path, install-fail rollback, build-fail rollback, crash-loop guard, and a target-sha-doesn't-exist rollback-failed terminal case. 5 mocha tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(updater): Playwright admin Apply / Cancel / Acknowledge flow Stubs /admin/update/status (and /admin/update/apply for the apply path) at the route level so we can assert UI transitions without actually running an update. Four scenarios: - Apply button POSTs and re-fetches status (>=2 status fetches total). - install-method-not-writable hides the button and shows localised denial copy. - rollback-failed terminal state shows the Acknowledge button and the "Manual intervention required" lastResult copy. - lockHeld=true hides Apply even when policy.canManual is on. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): admin banner shows rollback-failed terminal alert When execution.status === 'rollback-failed' the banner switches to a role=alert with the strong update.banner.terminal.rollback-failed copy and overrides the regular "update available" framing — an admin who left the system in this state needs to fix it before any other admin work matters. Other terminal states (preflight-failed, rolled-back) are informational and surface on the page itself, not the banner. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(updater): Tier 2 admin docs + manual smoke runbook + CHANGELOG doc/admin/updates.md gains a full Tier 2 section: prerequisites (git install + process supervisor with sample systemd unit), Apply flow with timings, every failure mode and the resulting state, the four endpoints, and the signature-verification opt-in. Settings table picks up the new updates.* knobs. docs/superpowers/specs/2026-04-25-auto-update-runbook.md is the manual smoke runbook the design spec calls for: disposable VM, systemd unit, every observable transition (happy path, install/ build-fail rollback, crash-loop guard, rollback-failed terminal, cancel during drain) plus a sign-off checklist for the release cut. CHANGELOG Unreleased section explains the supervisor requirement and points readers at the runbook. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(updater): note docker-friendly update flows as follow-up work Tier 2 refuses Apply on installMethod=docker because in-container mutation doesn't survive a container restart. Adds a future-work note covering the two reasonable paths for an in-product docker Apply button (instructions-only vs deploy-webhook) and explicitly rules out mounting /var/run/docker.sock as a footgun. Watchtower gets a pointer for admins who want fully autonomous docker updates today. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(updater): address Qodo review (1-6) + Playwright strict-mode CI fix 1. Tier 2 endpoints now gate on tier in {manual, auto, autonomous} — notify and off return 404 to match the prior PR-1 behaviour. Gate is evaluated per-request via app.use middleware so a settings.json reload takes effect without a full restart, and so integration tests can flip the tier dynamically. Adds a regression test that exercises 404 at tier=notify across all four endpoints. 2. cancel/apply race fixed: /admin/update/cancel no longer releases the lock — apply's finally block owns it for the request's lifetime. Apply now reloads state after preflight and aborts with 409 cancelled-during- preflight if execution.status is no longer 'preflight' for the same targetTag. Prevents a second apply from sneaking in while the first is still running its slow checks, and prevents the post-cancel apply from continuing into drain/execute. 3. SessionDrainer now restores acceptingConnections=true at drain completion (not just on cancel). The lock + persisted execution.status prevent a fresh apply from racing in — the in-memory flag was redundant safety that turned into a wedge if the executor threw post-drain. Adds a unit test asserting the flag is restored after natural drain end. 4. PadMessageHandler drain guard switched from socket.json.send (a socket.io v2/v3 API that may not exist on v4) to socket.emit('message', ...) for consistency with the other disconnect paths in the file. 5. Spawn 'error' handlers added to runStep helpers in UpdateExecutor and RollbackHandler, plus the gpg verify-tag spawn in trustedKeys. Without them, a missing/unexecutable binary leaves the promise hanging forever and the update flow stuck in-flight. SpawnFn type extended to allow on('error', ...) listeners cleanly. Spawn errors now resolve with code 1 + the error message in stderr, so the existing failure-detection branches fire normally. 6. executeUpdate body wrapped in try/catch. An exception from readSha, saveState, copyFile, or any step now lands in a rolling-back persist + returns failed-checkout, so the route's post-executor rollback path picks it up. State can no longer wedge at 'executing'. The catch's inner saveState is itself try/wrapped so a write-after-write failure doesn't crash the route either. CI: Playwright update-page-actions strict-mode violation fixed. Both the banner and the lastResult <p> contain "Manual intervention required"; selector now scopes to p.last-result-rollback-failed for the lastResult assertion specifically. 129 vitest unit tests + 23 mocha integration tests passing; ts-check clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(updater): address Qodo #7 (status leak) + #8 (short-drain values) #7. /admin/update/status now redacts diagnostic strings for unauth callers even when requireAdminForStatus is left at its default (false). Status enum + outcome enum are kept (the admin banner / pad-side badge need them to render the right UI) but execution.reason / execution.fromSha / execution.targetTag and the same fields on lastResult are stripped. Authed admin sessions still get the full payload — they're looking at their own server's diagnostics. Two new mocha tests cover both paths: "redacts execution.reason / lastResult.reason for unauth callers" and "returns full diagnostic payload to authed admin sessions". #8. SessionDrainer no longer schedules T-30 / T-10 broadcasts when the configured drainSeconds can't honour them. Previously, with drainSeconds < 30 the T-30 timer fired at zero remaining but the broadcast still claimed "30 seconds" — misleading. Now T-30 only schedules when drainSeconds > 30 and T-10 only when > 10. Admins picking a short drain get fewer announcements but each carries an accurate countdown. The opening announcement now reports the configured drain length rather than a hardcoded 60. Two updated unit tests: drainSeconds=15 (skips T-30, still fires T-10) and drainSeconds=5 (skips both). 131 vitest unit + 26 mocha integration tests passing; ts-check clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(updater): address Qodo follow-up — tag injection, rollback rejections, state validation Qodo posted three new concerns after the first fix push. 1. Git tag option injection (security). The release tag from GitHub's tag_name flowed into `git checkout` / `git verify-tag` as a positional arg. A tag starting with '-' would be parsed as an option and could bypass signature verification or change checkout semantics. Mitigated in three layers: - New refSafety helper (isValidTag / assertValidTag / refsTagsForm) enforces a strict subset of git's check-ref-format spec: rejects leading '-' or '.', whitespace, control chars, and ~ ^ : ? * [ \\ and the '..' sequence. - VersionChecker validates tag_name before persisting to state, so a malformed value from a misconfigured githubRepo never lands on disk. - UpdateExecutor calls assertValidTag and uses the refs/tags/<tag> form for git checkout. trustedKeys also validates and adds '--' to git verify-tag for an end-of-options marker. updateActions does an up-front isValidTag check on state.latest.tag so a corrupt state file gets a clean 409 instead of a 500. 2. Unhandled rollback rejections. checkPendingVerification was firing `void deps.saveState(...)` and `void performRollback(...)` without .catch(), so an fs error during boot's rollback path would bubble out as an unhandled rejection. Both callsites now go through fireSaveState / fireRollback helpers that catch and log; rollback rejections fall through to a best-effort terminal-state write + exit 75 so the supervisor can re-try the next boot with bootCount++. 3. Execution state under-validated. isValidExecution previously checked only that `status` was a known enum value, so a hand-edited state file with `{execution: {status: 'pending-verification'}}` (missing fromSha / targetTag / deadlineAt) would pass validation and reach RollbackHandler with undefined refs. The validator now consults a per-status required-fields map mirroring the ExecutionStatus union in types.ts and rejects empty strings as well as missing fields. Same tightening applied to lastResult.outcome (must be in the allowed enum, not just any string). Six new unit tests cover hand-edited corruption. 145 vitest + 26 mocha tests green; ts-check clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
85 KiB
Unreleased
Notable enhancements
- Self-update subsystem — Tier 2 (manual click).
- Admins on a git install can click "Apply update" at
/admin/update. Etherpad runs a 60s session drain (with T-60 / T-30 / T-10 broadcasts to every pad),git fetch / checkout / pnpm install --frozen-lockfile / pnpm run build:ui, and exits with code 75 so a process supervisor restarts it on the new version. The next boot runs a 60s health check; if/healthdoesn't come up the previous SHA + lockfile are restored automatically. - Crash-loop guard: if the new version reboots more than twice without the health check completing, RollbackHandler forces a rollback regardless of the timer.
- Terminal
rollback-failedstate surfaces a strong banner; the admin clicks Acknowledge once they've manually recovered to clear the lock and re-allow Tier 2 attempts. - New settings under
updates.*:preApplyGraceMinutes,drainSeconds,rollbackHealthCheckSeconds,diskSpaceMinMB,requireSignature,trustedKeysPath. Tag signature verification is opt-in (defaultfalse) — seedoc/admin/updates.mdfor the keyring setup. - A process supervisor (systemd / pm2 / docker
--restart=unless-stopped) is required to apply updates. Without one, exit 75 leaves the instance down. - Tiers 3 (auto with grace window) and 4 (autonomous in maintenance window) remain designed but unimplemented and will land in subsequent releases.
- Admins on a git install can click "Apply update" at
2.7.3
Breaking changes
- Minimum required Node.js version is now 22.13. Node.js 20 is reaching end-of-life (see https://nodejs.org/en/about/previous-releases) and pnpm 11 hard-rejects Node releases older than 22.13. The CI matrix targets Node 22, 24, and 25. Upgrading should be straightforward — install a current Node.js release before updating Etherpad.
- The official Docker image no longer ships
curl,npm, ornpx. These were dropped to remove transitive CVEs (curl/libcurl SMB advisories, npm's bundled picomatch 4.0.3 and brace-expansion 2.0.2). The container's healthcheck now useswget(busybox built-in, always present), and Etherpad provisionspnpmviacorepackfor all runtime package operations. If you exec into the container and rely oncurlornpmfor ad-hoc tasks, install them on demand withapk add curlor use the busyboxwget/pnpmalready present.
Notable enhancements
- GDPR / privacy controls. A multi-PR series adds the building blocks operators need to satisfy data-subject requests:
- Pad deletion controls (admin-driven and self-service).
- IP / privacy audit pass across the codebase.
- Author-token cookies are now
HttpOnly, removing them from JavaScript reach. - Configurable privacy banner shown on first visit.
- Author erasure: an authenticated path for purging an individual author's identity and contributions.
- Self-update subsystem (Tier 1: notify).
- Periodic check against the GitHub Releases API for the configured repo (default
ether/etherpad). Configurable via the newupdates.*settings block, default tier"notify". Setupdates.tierto"off"to disable entirely. - The admin UI shows a banner and a dedicated "Etherpad updates" page with the current version, latest version, install method, and changelog.
- Pad users see a discreet footer badge only when the running version is severely outdated (one or more major versions behind) or flagged as vulnerable in a recent release manifest. The public endpoint that drives this never leaks the version string itself.
- New top-level
adminEmailsetting. When set, the updater emails the admin on first detection of severe / vulnerable status, with escalating cadence (weekly while vulnerable, monthly while severely outdated). PR 1 ships the dedupe + cadence logic; real SMTP wiring lands in a follow-up PR. - Tier 1 ships in this release. Tiers 2 (manual click), 3 (auto with grace window) and 4 (autonomous in maintenance window) are designed and will land in subsequent releases.
- See
doc/admin/updates.mdfor full configuration.
- Periodic check against the GitHub Releases API for the configured repo (default
- Pad compaction. New
compactPadHTTP API plusbin/compactPadandbin/compactAllPadsCLIs to reclaim database space on long-lived pads with heavy edit history (issue #6194).--keep Nretains the last N revisions;--dry-runpreviews per-pad rev counts before writing. Per-pad failures don't stop the bulk run. - New packaging targets.
- Etherpad is now published as a Snap package.
- Debian (.deb) packages are built via nfpm with a systemd unit, and a signed apt repository is published to
etherpad.org/apt.
- Editor enhancements.
- IDE-style line operations: keyboard shortcuts to duplicate or delete the current line.
- New
showMenuRightURL parameter to hide the right-side toolbar — useful for embeds that need slimmer chrome. - Click a user in the userlist to open chat with
@<name>prefilled, making mentions discoverable. - New
padOptions.fadeInactiveAuthorColorssetting plus a toolbar UI to fade the background colors of authors who have left the pad.
- Color contrast. Author colors now pick the WCAG-higher-contrast text color for readability.
- Social / mobile metadata. Pad, timeslider, and home views now emit Open Graph and Twitter Card tags (closes #7599) and a
theme-colormeta that matches the toolbar on mobile. - Plugin admin UX. The
/adminplugin browser surfaces each plugin'sep.jsondisablesdeclarations, so operators can see what a plugin will turn off before installing.
Notable fixes
- Socket.io: don't kick authenticated duplicate-author sessions. A regression where two tabs from the same authenticated author could evict each other has been fixed (#7656 / #7678).
- Anchor scrolling. Anchor-link navigation now waits for layout to settle, so jumping to a deep link no longer overshoots.
- Plugin updater.
bin/updatePlugins.shactually updates installed plugins again (closes #6670). - Settings: stable per-release version string.
randomVersionStringis now derived from the release identity rather than regenerated on each boot, so caches behave correctly across restarts of the same version.
Internal / contributor-facing
- The HTTP client in the backend has been migrated from
axiosto the built-infetchAPI, dropping a dependency now that Node 22 ships a stable fetch. admin/andui/workspaces moved fromrolldown-viteto upstream Vite 8.- Build and CI moved to pnpm 11 (
packageManager: "pnpm@11.0.6"); theDockerfile, snap, and all GitHub workflows are aligned. pnpm overrides have been migrated frompackage.jsontopnpm-workspace.yamlto match pnpm 11's expectations. - All client modules have been converted to ESM.
- The CI matrix tests Node 22, 24, and 25; on PRs the matrix is reduced to a single Node version to keep feedback fast.
- Frontend Playwright tests now run against the
/etherplugin set, with feature-tag based skips so plugin-incompatible specs are excluded automatically. - Build hardening: signed apt repo publishing, frozen lockfile installs across CI, Node setup pinned in every workflow, and a Docker-image CVE sweep that bumps
npm,pnpm, anduuid.
Localisation
- Multiple updates from translatewiki.net.
2.7.2
Notable enhancements and fixes
- Accessibility pass: corrected dialog semantics, improved focus management, added missing icon labels, and set the
html langattribute correctly. - Chat: clicking the chat icon works again, disabled toggles render properly, and the username layout no longer overflows.
/export/etherpadnow honors the:revURL segment, so revision-specific exports return the requested revision instead of the latest.- Undo / redo now scrolls the viewport to follow the caret, so reverted edits stay in view.
- Page Down / Page Up now scrolls by viewport height instead of a fixed line count, matching standard editor behavior on tall and short windows alike.
- Editbar: caret is restored to the pad after changing a toolbar select, so typing continues in the document instead of falling through to the toolbar.
- Admin: i18n is restored on
/adminso the admin UI is translated again.
2.7.1
Notable enhancements and fixes
- fixed stop harcoding lang=en, letting the client auto detect locale
- Stop mutating the shared plugin registry during sanitization
- Preserve non-breaking space
2.7.0
Breaking changes
- Abiword has been replaced with LibreOffice for document import/export. If you were using Abiword for DOCX/ODT/PDF conversion, update your
settings.jsonto pointsofficeat your LibreOffice binary. DOCX export is now supported out of the box.
Notable enhancements
- Added line numbers to the timeslider so you can follow along with specific lines while replaying a pad's history.
- Added a playback speed setting to the timeslider — you can now scrub through history faster (or slower) than real time.
- Creator-owned pad settings defaults: the user who creates a pad now seeds its default settings, giving pad creators more control over initial configuration.
- Cookie names are now configurable via a prefix setting. Useful when running multiple Etherpads on the same domain and you need to keep their session cookies from colliding.
- Added a new
aceRegisterLineAttributeshook so plugins can preserve custom line attributes across Enter / line-split operations. Documentation for the hook is included. - Added a one-line installer script for getting Etherpad running quickly on a fresh machine.
- Docker images are now published to GitHub Container Registry (GHCR) in addition to Docker Hub.
- npm publishing of core and plugins has been migrated to OIDC trusted publishing for stronger supply-chain security.
Notable fixes
- Database drivers are now bundled with Etherpad again, so fresh installs no longer fail to connect to Postgres, MySQL, and friends out of the box. A regression test has been added to prevent this from breaking again.
- Pending changesets are now flushed immediately after a reconnect instead of being silently dropped, and users are warned when a pending edit is not accepted by the server.
- Head revision and atext are now captured atomically, preventing the occasional "mismatched apply" errors on busy pads.
- Clearing authorship colors can now be undone without forcing a client disconnect.
- Added periodic cleanup of expired/stale sessions from the database, and fixed a race condition in the session cleanup timeout.
- Error messages returned to clients are now sanitized by default with deduplication, so internal details no longer leak through error responses.
- Raised the maximum socket.io message size to 10 MB so large pastes no longer get rejected.
- Dev mode entrypoint paths now respect the
x-proxy-pathheader, fixing reverse-proxy setups in development. - Numerous list-related fixes: numbered list wrapped lines now indent correctly, ordered list numbering is preserved across bullet interruptions during export, consecutive numbering survives indented sub-bullets, switching from unordered to ordered resets numbering, and line attributes are preserved across drag-and-drop.
- Bold (and other) formatting is now retained after copy-paste.
- Dead-key / compose-key input no longer eats the preceding space.
POSTAPI requests with a JSON body no longer time out.appendTextnow correctly attributes the new text to the specified author.createDiffHTMLno longer fails withNot a changeset: undefined.- Added
padIdto thepadUpdate/padCreatehook context. - Fixed
numConnectedUsersto include the joining user in its count. - Accessibility improvements: keyboard trap fix, better screen reader support, and
aria-liveannouncements. - RTL URL parameter
rtl=falsenow correctly disables RTL mode. - Language dropdown is now sorted alphabetically by native name.
- PageDown now advances the caret by a full page of lines.
- ESM/CJS interop issues in the Settings module that had been breaking plugin compatibility have been resolved, with setters added to the CJS compatibility layer and regression tests in place.
- Several Docker build fixes: git submodule handling,
hardlinkpackage-import-method for ZFS, and production-only workspace config.
Other
- Many occurrences of "etherpad-lite" have been renamed to "etherpad" across the codebase and documentation.
- Pinned 33 transitive dependencies to patched versions to clear out Dependabot security alerts.
- Restricted
GITHUB_TOKENpermissions in the update-plugins workflow.
2.6.1
For those wondering where the new updates are and why it was very quite throughout the last 1 1/2 years – I've been working on a new implementation of Etherpad from scratch in Go. It's called Etherpad-Go and you can find it here: https://github.com/ether/etherpad-go
and a short FAQ about it here: https://github.com/ether/etherpad-go/wiki/FAQ . I'd love to hear your feedback about it either on Discord or issue tracker. There is a README.md that explains how to get started and try it out and also the FAQ can be quite fruitful. Latest release can be found here: https://github.com/ether/etherpad-go/releases/tag/v0.0.4
Notable enhancements and fixes of this release
- Minor fixes and improvements to the session transfer feature introduced in 2.6.0
- Dependencies upgrades
2.6.0
Notable enhancements and fixes
- Added native option to transfer your Etherpad session between browsers. If you use multiple browsers or different PC for Etherpad they are different sessions. Meaning typing on one PC and then switching to another one in the same pad will result in different authorship colors. With this new feature you can now transfer your session to another browser or PC. To do so, open the home page and click on the wheel icon in the top right corner. After that click through the first dialog prompting you to copy a code to your clipboard. On your second browser open the same dialog and switch to "Receive Session" tab. There you can paste the code you copied before and click on "Receive Session". After that your session is transferred, and you can continue editing with the same authorship color as before. Just be aware that you can't have two active sessions at once in a pad.
- Updated to oidc provider v2.6.0 after resolving compatibility issues.
🎉 For all the people celebrating: Have a happy and awesome new year! 🎉 There is something big on the horizon for Etherpad in 2026. Stay tuned!
2.5.3
Notable enhancements and fixes
- Fixed an issue with the release script that caused the release to not be created correctly.
2.5.2
Notable enhancements and fixes
- Fixes the no skin theme having an overlapping
- Adds a new setting to disable recent pads to be shown. By setting
showRecentPadsto false in thesettings.jsonfile you can disable the recent pads feature on the home screen. - Sets the oidc-provider version to 9.5.1 as 9.5.2 crashes Etherpad on startup.
2.5.1
Notable enhancements and fixes
-
Added endpoint for prometheus scraping. You can now scrape the metrics endpoint with prometheus. It is available at /stats/prometheus if you have enableMetrics set to true in your settings.json
-
fixed exposeVersion causing the pad panel to not load correctly
-
fixed admin manage pad url to also take the base path into account
2.5.0
Notable enhancements and fixes
- Updated to express 5.0.0. This is a major update to express that brings a lot of improvements and fixes. Please update all your plugins to the latest version to ensure compatibility. A lot changed in the route matching, and thus old plugins will throw errors and crash Etherpad.
- Fixed an issue with the no-skin theme with cookie recentPadList feature
- Fixed layout issues with the no-skin theme
2.4.2
Notable enhancements and fixes
- Fixed a german translation in the english translation file.
2.4.1
Notable enhancements and fixes
- Added generating release through ci cd pipeline.
- Readded temporarily disabled workflows after release generation works again
2.4.0
Notable enhancements and fixes
- Added home button to the pad panel. To show it in your current instance, please copy the updated "toolbar" settings from the
settings.json.templatefile to yoursettings.jsonfile. - Added more current design for the default collibri theme.
- Added handling of recent visited pads in the collibri theme. You can now access the most recent three pads you visited in the pad panel.
- Disable stats endpoints if enableMetrics is set to false. This allows you to disable the metrics endpoints if you don't want to use them.
- Use Node LTS instead of always latest Node version.
2.3.2
Notable enhancements and fixes
- Fixed admin ui displaying incorrect text
2.3.1
Notable enhancements and fixes
- Dependency updates
2.3.0
Notable enhancements and fixes
- Added possibility to cluster Etherpads behind reverse proxy. There is now a new reverse proxy designed for Etherpads that handles multiple Etherpads and the created pads in them. It will assign the pad assignement to an Etherpad at random but once the choice was made it will always reverse proxy the same backend. This allows to host multiple concurrent Etherpads and benefit from multi core systems even though one Etherpad is singlethreaded.
- Added reverse proxy configuration for replacing Nginx. In the past there were some issues with nginx and its configuration. This reverse proxy allows you to handle your configuration with ease.
If you want to find out more about the reverse proxy method check out the repository https://github.com/ether/etherpad-proxy . It also contains a sample docker-compose file with three Etherpads and one etherpad-proxy. Of course you need to adapt the settings.json.template to your liking and map it into the reverse proxy image before you are ready :).
- Added client authorization to work with Etherpad. Before it would get blocked because it doesn't have the required claim. As this is now fixed etherpad-proxy can also work with your new OAuth2 configuration and retrieve a token via client credentials flow.
2.2.7
Notable enhancements and fixes
- We migrated all important pages to React 19 and React Router v7
Besides that only dependency updates.
-> Have a merry Christmas and a happy new year. 🎄 🎁
2.2.6
Notable enhancements and fixes
- Added option to delete a pad by the creator. This option can be found in the settings menu. When you click on it you get a confirm dialog and after that you have the chance to completely erase the pad.
2.2.5
Notable enhancements and fixes
- Fixed timeslider not scrolling when the revision count is a multiple of 100
- Added new Restful API for version 2 of Etherpad. It is available at /api-docs
2.2.4
Notable enhancements and fixes
- Switched to new SQLite backend
- Fixed rusty-store-kv module not found
2.2.3
Notable enhancements and fixes
- Introduced a new in process database
rustydbthat represents a fast key value store written in Rust. - Readded window._ as a shortcut for getting text
- Added support for migrating any ueberdb database to another. You can now switch as you please. See here: https://docs.etherpad.org/cli.html
- Further Typescript movements
- A lot of security issues fixed and reviewed in this release. Please update.
2.2.2
Notable enhancements and fixes
- Removal of Etherpad require kernel: We finally managed to include esbuild to bundle our frontend code together. So no matter how many plugins your server has it is always one JavaScript file. This boosts performance dramatically.
- Added log layoutType: This lets you print the log in either colored or basic (black and white text)
- Introduced esbuild for bundling CSS files
- Cache all files to be bundled in memory for faster load speed
2.1.1
Notable enhancements and fixes
- Fixed failing Docker build when checked out as git submodule. Thanks to @neurolabs
- Fixed: Fallback to websocket and polling when unknown(old) config is present for socket io
- Fixed: Next page disabled if zero page by @samyakj023
- On CTRL+CLICK bring the window back to focus by Helder Sepulveda
2.1.0
Notable enhancements and fixes
- Added PWA support. You can now add your Etherpad instance to your home screen on your mobile device or desktop.
- Fixed live plugin manager versions clashing. Thanks to @yacchin1205
- Fixed a bug in the pad panel where pagination was not working correctly when sorting by pad name
Compatibility changes
- Reintroduced APIKey.txt support. You can now switch between APIKey and OAuth2.0 authentication. This can be toggled with the setting authenticationMethod. The default is OAuth2. If you want to use the APIKey method you can set that to
apikey.
2.0.3
Notable enhancements and fixes
- Added documentation for replacing apikeys with oauth2
- Bumped live plugin manager to 0.20.0. Thanks to @fgreinacher
- Added better documentation for using docker-compose with Etherpad
2.0.2
Notable enhancements and fixes
- Fixed the locale loading in the admin panel
- Added OAuth2.0 support for the Etherpad API. You can now log in into the Etherpad API with your admin user using OAuth2
Compatibility changes
- The tests now require generating a token from the OAuth secret. You can find the
generateJWTTokenin the common.ts script for plugin endpoint updates.
2.0.1
Notable enhancements and fixes
- Fixed a bug where a plugin depending on a scoped dependency would not install successfully.
2.0.0
Compatibility changes
- Socket io has been updated to 4.7.5. This means that the json.send function won't work anymore and needs to be changed to .emit('message', myObj)
- Deprecating npm version 6 in favor of pnpm: We have made the decision to switch to the well established pnpm (https://pnpm.io/). It works by symlinking dependencies into a global directory allowing you to have a cleaner and more reliable environment.
- Introducing Typescript to the Etherpad core: Etherpad core logic has been rewritten in Typescript allowing for compiler checking of errors.
- Rewritten Admin Panel: The Admin panel has been rewritten in React and now features a more pleasant user experience. It now also features an integrated pad searching with sorting functionality.
Notable enhancements and fixes
-
Bugfixes
- Live Plugin Manager: The live plugin manager caused problems when a plugin had depdendencies defined. This issue is now resolved.
-
Enhancements
- pnpm Workspaces: In addition to pnpm we introduced workspaces. A clean way to manage multiple bounded contexts like the admin panel or the bin folder.
- Bin folder: The bin folder has been moved from the src folder to the root folder. This change was necessary as the contained scripts do not represent core functionality of the user.
- Starting Etherpad: Etherpad can now be started with a single command:
pnpm run prodin the root directory. - Installing Etherpad: Etherpad no longer symlinks itself in the root directory. This is now also taken care by pnpm, and it just creates a node_modules folder with the src directory`s ep_etherpad-lite folder
- Plugins can now be installed simply via the command:
pnpm run plugins i first-plugin second-pluginor if you want to install from path you can do:pnpm run plugins i --path ../path-to-plugin
1.9.7
Notable enhancements and fixes
- Added Live Plugin Manager: Plugins are now installed into a separate folder on the host system. This folder is called
plugin_packages. That way the plugins are separated from the normal etherpad installation. - Make repairPad.js more verbose
- Fixed favicon not being loaded correctly
1.9.6
Notable enhancements and fixes
- Prevent etherpad crash when update server is not reachable
- Use npm@6 in Docker build
- Fix setting the log level in settings.json
1.9.5
Compatibility changes
- This version deprecates NodeJS16 as it reached its end of life and won't receive any updates. So to get started with Etherpad v1.9.5 you need NodeJS 18 and above.
- The bundled windows NodeJS version has been bumped to the current LTS version 20.
Notable enhancements and fixes
- The support for the tidy program to tidy up HTML files has been removed. This decision was made because it hasn't been updated for years and also caused an incompability when exporting a pad with Abiword.
1.9.4
Compatibility changes
- Log4js has been updated to the latest version. As it involved a bump of 6 major version. A lot has changed since then. Most notably the console appender has been deprecated. You can find out more about it here
Notable enhancements and fixes
- Fix for MySQL: The logger calls were incorrectly configured leading to a crash when e.g. somebody uses a different encoding than standard MySQL encoding.
1.9.3
Compability changes
- express-rate-limit has been bumped to 7.0.0: This involves the breaking change that "max: 0" in the importExportRateLimiting is set to always trigger. So set it to your desired value. If you haven't changed that value in the settings.json you are all set.
Notable enhancements and fixes
-
Bugfixes
- Fix etherpad crashing with mongodb database
-
Enhancements
- Add surrealdb database support. You can find out more about this database here.
- Make sqlite faster: The sqlite library has been switched to better-sqlite3. This should lead to better performance.
1.9.2
Notable enhancements and fixes
-
Security
- Enable session key rotation: This setting can be enabled in the settings.json. It changes the signing key for the cookie authentication in a fixed interval.
-
Bugfixes
- Fix appendRevision when creating a new pad via the API without a text.
-
Enhancements
- Bump JQuery to version 3.7
- Update elasticsearch connector to version 8
Compatibility changes
- No compability changes as JQuery maintains excellent backwards compatibility.
For plugin authors
- Please update to JQuery 3.7. There is an excellent deprecation guide over here. Version 3.1 to 3.7 are relevant for the upgrade.
1.9.1
Notable enhancements and fixes
-
Security
- Limit requested revisions in timeslider and export to head revision. (affects v1.9.0)
-
Bugfixes
- revisions in
CHANGESET_REQ(timeslider) and export (txt, html, custom) are now checked to be numbers. - bump sql for audit fix
- revisions in
-
Enhancements
- Add keybinding meta-backspace to delete to beginning of line
- Fix automatic Windows build via GitHub Actions
- Enable docs to be build cross platform thanks to asciidoctor
Compatibility changes
- tests: drop windows 7 test coverage & use chrome latest for admin tests
- Require Node 16 for Etherpad and target Node 20 for testing
1.9.0
Notable enhancements and fixes
- Windows build:
- The bundled
node.exewas upgraded from v12 to v16. - The bundled
node.exeis now a 64-bit executable. If you need the 32-bit version you must download and install Node.js yourself.
- The bundled
- Improvements to login session management:
express_sidcookies andsessionstorage:*database records are no longer created unlessrequireAuthenticationistrue(or a plugin causes them to be created).- Login sessions now have a finite lifetime by default (10 days after leaving).
sessionstorage:*database records are automatically deleted when the login session expires (with some exceptions that will be fixed in the future).- Requests for static content (e.g.,
/robots.txt) and special pages (e.g., the HTTP API,/stats) no longer create login session state. - The secret used to sign the
express_sidcookie is now automatically regenerated every day (called key rotation) by default. If key rotation is enabled, the now-deprecatedSESSIONKEY.txtfile can be safely deleted after Etherpad starts up (its content is read and saved to the database and used to validate signatures from old cookies until they expire).
- The following settings from
settings.jsonare now applied as expected (they were unintentionally ignored before):padOptions.langpadOptions.showChatpadOptions.userColorpadOptions.userName
- HTTP API:
- Fixed the return value of
getTextwhen called with a specific revision. - Fixed a potential attribute pool corruption bug with
copyPadWithoutHistory. - Mappings created by
createGroupIfNotExistsForare now removed from the database when the group is deleted. - Fixed race conditions in the
setText,appendText, andrestoreRevisionfunctions. - Added an optional
authorIdparameter toappendText,copyPadWithoutHistory,createGroupPad,createPad,restoreRevision,setHTML, andsetText, and bumped the latest API version to 1.3.0.
- Fixed the return value of
- Fixed a crash if the database is busy enough to cause a query timeout.
- New
/healthendpoint for getting information about Etherpad's health (see draft-inadarei-api-health-check-06). - Docker now uses the new
/healthendpoint for health checks, which avoids issues when authentication is enabled. It also avoids the unnecessary creation of database records for managing browser sessions. - When copying a pad, the pad's records are copied in batches to avoid database timeouts with large pads.
- Exporting a large pad to
.etherpadformat should be faster thanks to bulk database record fetches. - When importing an
.etherpadfile, records are now saved to the database in batches to avoid database timeouts with large pads.
For plugin authors
- New
expressPreSessionserver-side hook. - Pad server-side hook changes:
padCheck: New hook.padCopy: NewsrcPadanddstPadcontext properties.padDefaultContent: New hook.padRemove: Newpadcontext property.
- The
dbproperty on Pad objects is now public. - New
getAuthorIdserver-side hook. - New APIs for processing attributes:
ep_etherpad-lite/static/js/attributes(low-level API) andep_etherpad-lite/static/js/AttributeMap(high-level API). - The
importserver-side hook has a newImportErrorcontext property. - New
exportEtherpadandimportEtherpadserver-side hooks. - The
handleMessageSecurityandhandleMessageserver-side hooks have a newsessionInfocontext property that includes the user's author ID, the pad ID, and whether the user only has read-only access. - The
handleMessageSecurityserver-side hook can now be used to grant write access for the current message only. - The
init_<pluginName>server-side hooks have a newloggercontext property that plugins can use to log messages. - Prevent infinite loop when exiting the server
- Bump dependencies
Compatibility changes
- Node.js v14.15.0 or later is now required.
- The default login session expiration (applicable if
requireAuthenticationistrue) changed from never to 10 days after the user leaves.
For plugin authors
- The
clientcontext property for thehandleMessageSecurityandhandleMessageserver-side hooks is deprecated; use thesocketcontext property instead. - Pad server-side hook changes:
padCopy:- The
originalPadcontext property is deprecated; usesrcPadinstead. - The
destinationIDcontext property is deprecated; usedstPad.idinstead.
- The
padCreate: Theauthorcontext property is deprecated; use the newauthorIdcontext property instead. Also, the hook now runs asynchronously.padLoad: Now runs when a temporary Pad object is created during import. Also, it now runs asynchronously.padRemove: ThepadIDcontext property is deprecated; usepad.idinstead.padUpdate: Theauthorcontext property is deprecated; use the newauthorIdcontext property instead. Also, the hook now runs asynchronously.
- Returning
truefrom ahandleMessageSecurityhook function is deprecated; return'permitOnce'instead. - Changes to the
src/static/js/Changeset.jslibrary:- The following attribute processing functions are deprecated (use the new
attribute APIs instead):
attribsAttributeValue()eachAttribNumber()makeAttribsString()opAttributeValue()
opIterator(): Deprecated in favor of the newdeserializeOps()generator function.appendATextToAssembler(): Deprecated in favor of the newopsFromAText()generator function.newOp(): Deprecated in favor of the newOpclass.
- The following attribute processing functions are deprecated (use the new
attribute APIs instead):
- The
AuthorManager.getAuthor4Token()function is deprecated; use the newAuthorManager.getAuthorId()function instead. - The exported database records covered by the
exportEtherpadAdditionalContentserver-side hook now include keys like${customPrefix}:${padId}:*, not just${customPrefix}:${padId}. - Plugin locales should overwrite core's locales Stale
- Plugin locales overwrite core locales
1.8.18
Released: 2022-05-05
Notable enhancements and fixes
- Upgraded ueberDB to fix a regression with CouchDB.
1.8.17
Released: 2022-02-23
Security fixes
- Fixed a vunlerability in the
CHANGESET_REQmessage handler that allowed a user with any access to read any pad if the pad ID is known.
Notable enhancements and fixes
- Fixed a bug that caused all pad edit messages received at the server to go through a single queue. Now there is a separate queue per pad as intended, which should reduce message processing latency when many pads are active at the same time.
1.8.16
Security fixes
If you cannot upgrade to v1.8.16 for some reason, you are encouraged to try cherry-picking the fixes to the version you are running:
git cherry-pick b7065eb9a0ec..77bcb507b30e
- Maliciously crafted
.etherpadfiles can no longer overwrite arbitrary non-pad database records when imported. - Imported
.etherpadfiles are now subject to numerous consistency checks before any records are written to the database. This should help avoid denial-of-service attacks via imports of malformed.etherpadfiles.
Notable enhancements and fixes
- Fixed several
.etherpadimport bugs. - Improved support for large
.etherpadimports.
1.8.15
Security fixes
- Fixed leak of the writable pad ID when exporting from the pad's read-only ID. This only matters if you treat the writeable pad IDs as secret (e.g., you are not using ep_padlist2) and you share the pad's read-only ID with untrusted users. Instead of treating writeable pad IDs as secret, you are encouraged to take advantage of Etherpad's authentication and authorization mechanisms (e.g., use ep_openid_connect with ep_readonly_guest, or write your own authentication and authorization plugins).
- Updated dependencies.
Compatibility changes
- The
logconfigsetting is deprecated.
For plugin authors
- Etherpad now uses jsdom instead of
cheerio for processing HTML imports. There are two
consequences of this change:
require('ep_etherpad-lite/node_modules/cheerio')no longer works. To fix, your plugin should directly depend oncheerioand dorequire('cheerio').- The
collectContentImagehook'snodecontext property is now anHTMLImageElementobject rather than a Cheerio Node-like object, so the API is slightly different. See citizenos/ep_image_upload#49 for an example fix.
- The
clientReadyserver-side hook is deprecated; use the newuserJoinhook instead. - The
init_<pluginName>server-side hooks are now run every time Etherpad starts up, not just the first time after the named plugin is installed. - The
userLeaveserver-side hook's context properties have changed:auth: Deprecated.author: Deprecated; use the newauthorIdproperty instead.readonly: Deprecated; use the newreadOnlyproperty instead.rev: Deprecated.
- Changes to the
src/static/js/Changeset.jslibrary:opIterator(): The unused start index parameter has been removed, as has the unusedlastIndex()method on the returned object.smartOpAssembler(): The returned object'sappendOpWithText()method is deprecated without a replacement available to plugins (if you need one, let us know and we can make the privateopsFromText()function public).- Several functions that should have never been public are no longer exported:
applyZip(),assert(),clearOp(),cloneOp(),copyOp(),error(),followAttributes(),opString(),stringOp(),textLinesMutator(),toBaseTen(),toSplices().
Notable enhancements and fixes
- Accessibility fix for JAWS screen readers.
- Fixed "clear authorship" error (see issue #5128).
- Etherpad now considers square brackets to be valid URL characters.
- The server no longer crashes if an exception is thrown while processing a message from a client.
- The
useMonospaceFontGlobalsetting now works (thanks @Lastpixl!). - Chat improvements:
- The message input field is now a text area, allowing multi-line messages (use shift-enter to insert a newline).
- Whitespace in chat messages is now preserved.
- Docker improvements:
- New
HEALTHCHECKinstruction (thanks @Gared!). - New
settings.jsonvariables:DB_COLLECTION,DB_URL,SOCKETIO_MAX_HTTP_BUFFER_SIZE,DUMP_ON_UNCLEAN_EXIT(thanks @JustAnotherArchivist!). .ep_initializedfiles are no longer created.
- New
- Worked around a Firefox Content Security Policy
bug that caused CSP
failures when
'self'was in the CSP header. See issue #4975 for details. - UeberDB upgraded from v1.4.10 to v1.4.18. For details, see the ueberDB
changelog.
Highlights:
- The
postgrespooldriver was renamed topostgres, replacing the old driver of that name. If you used the oldpostgresdriver, you may see an increase in the number of database connections. - For
postgres, you can now set thedbSettingsvalue insettings.jsonto a connection string (e.g.,"postgres://user:password@host/dbname") instead of an object. - For
mongodb, thedbNamesetting was renamed todatabase(butdbNamestill works for backwards compatibility) and is now optional (if unset, the database name inurlis used).
- The
/admin/settingsnow honors the--settingscommand-line argument.- Fixed "Author X tried to submit changes as author Y" detection.
- Error message display improvements.
- Simplified pad reload after importing an
.etherpadfile.
For plugin authors
clientVarswas added to the context for thepostAceInitclient-side hook. Plugins should use this instead of theclientVarsglobal variable.- New
userJoinserver-side hook. - The
userLeaveserver-side hook has a newsocketcontext property. - The
helper.aNewPad()function (accessible to client-side tests) now accepts hook functions to inject when opening a pad. This can be used to test any new client-side hooks your plugin provides. - Chat improvements:
- The
chatNewMessageclient-side hook context has new properties:message: Provides access to the raw message object so that plugins can see the original unprocessed message text and any added metadata.rendered: Allows plugins to completely override how the message is rendered in the UI.
- New
chatSendMessageclient-side hook that enables plugins to process the text before sending it to the server or augment the message object with custom metadata. - New
chatNewMessageserver-side hook to process new chat messages before they are saved to the database and relayed to users.
- The
- Readability improvements to browser-side error stack traces.
- Added support for socket.io message acknowledgments.
1.8.14
Security fixes
- Fixed a persistent XSS vulnerability in the Chat component. In case you can't
update to 1.8.14 directly, we strongly recommend to cherry-pick
a796811558. Thanks to sonarsource for the professional disclosure.
Compatibility changes
- Node.js v12.13.0 or later is now required.
- The
faviconsetting is now interpreted as a pathname to a favicon file, not a URL. Please see the documentation comment insettings.json.template. - The undocumented
faviconPadandfaviconTimeslidersettings have been removed. - MySQL/MariaDB now uses connection pooling, which means you will see up to 10 connections to the MySQL/MariaDB server (by default) instead of 1. This might cause Etherpad to crash with a "ER_CON_COUNT_ERROR: Too many connections" error if your server is configured with a low connection limit.
- Changes to environment variable substitution in
settings.json(see the documentation comments insettings.json.templatefor details):- An environment variable set to the string "null" now becomes
nullinstead of the string "null". Similarly, if the environment variable is unset and the default value is "null" (e.g.,"${UNSET_VAR:null}"), the value now becomesnullinstead of the string "null". It is no longer possible to produce the string "null" via environment variable substitution. - An environment variable set to the string "undefined" now causes the setting
to be removed instead of set to the string "undefined". Similarly, if the
environment variable is unset and the default value is "undefined" (e.g.,
"${UNSET_VAR:undefined}"), the setting is now removed instead of set to the string "undefined". It is no longer possible to produce the string "undefined" via environment variable substitution. - Support for unset variables without a default value is now deprecated.
Please change all instances of
"${FOO}"in yoursettings.jsonto${FOO:null}to keep the current behavior. - The
DB_*variable substitutions insettings.json.dockerthat previously defaulted tonullnow default to "undefined".
- An environment variable set to the string "null" now becomes
- Calling
nextwithout argument when usingChangeset.opIteratordoes always return a new Op. Seeb9753dcc71for details.
Notable enhancements and fixes
- MySQL/MariaDB now uses connection pooling, which should improve stability and reduce latency.
- Bulk database writes are now retried individually on write failure.
- Minify: Avoid crash due to unhandled Promise rejection if stat fails.
- padIds are now included in /socket.io query string, e.g.
https://video.etherpad.com/socket.io/?padId=AWESOME&EIO=3&transport=websocket&t=...&sid=.... This is useful for directing pads to separate socket.io nodes.