66 Commits

Author SHA1 Message Date
John McLear
4bda757304
feat(api): public compactPad API + bin/compactPad CLI over existing Cleanup (#7567)
* feat(pad): compactHistory() + compactPad CLI for DB-size reclaim

Fixes #6194. Long-lived pads with heavy edit history dominate the DB —
the issue describes a ~400 MB Postgres after two months with ~100
users. Etherpad keeps every revision forever, and removing arbitrary
middle revisions is unsafe because state is reconstructed by composing
forward from key revisions.

What's safe: collapse the full history into a single base revision
that reproduces the current atext. The existing `copyPadWithoutHistory`
already does this for a new pad ID — this PR lifts that same changeset
pattern into an in-place operation and wires up an admin CLI.

- `Pad.compactHistory(authorId?)` (src/node/db/Pad.ts): composes the
  current atext into one base changeset, deletes all existing rev
  records, clears saved-revision bookmarks, and appends the new rev 0.
  Text, attributes, and chat history are preserved; saved-revision
  pointers are cleared. Returns the number of revisions removed.
- `API.compactPad(padID, authorId?)` (src/node/db/API.ts): public-API
  wrapper around compactHistory. Reports `{removed}` so callers can
  log savings.
- `APIHandler.ts`: register `compactPad` under a new `1.3.1` version,
  bump `latestApiVersion`.
- `bin/compactPad.ts`: admin CLI. Reports the current revision count,
  calls compactPad via the HTTP API, and prints how many revisions
  were dropped.
- `src/tests/backend/specs/compactPad.ts`: four backend tests cover
  the empty-pad no-op, the text-preservation + head=0 contract,
  saved-revision cleanup, and that subsequent edits continue to
  append cleanly on top of the collapsed base.

The operation is destructive so admins must opt in explicitly; the CLI
prints the before-count, and the recommended pre-flight is an
`.etherpad` export (backup).

Closes #6194

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(compact): delegate to copyPadWithoutHistory via temp-pad swap

The initial compactHistory() implementation built a custom base
changeset and re-ran appendRevision against a reset atext — but the
changeset was packed with oldLength=2 (matching copyPadWithoutHistory's
dest-pad init state) while the reset atext was only length 1, so
applyToText tripped its "mismatched apply: 1 / 2" assertion and every
test failed with a Changeset corruption error.

Switch to the tested path instead: copy the pad via
copyPadWithoutHistory to a uniquely-named temp pad (inherits all its
attribute/pool/changeset correctness), read the temp pad's rev records
back, delete the old ones under our pad's ID, write the new records in
their place, update in-memory state to match, and remove the temp pad.
Errors at any step fall through with a best-effort temp-pad cleanup.

Contract shifts slightly: the collapsed pad is head<=1 rather than
head=0, matching the shape of a freshly-imported pad (seed rev 0 +
content rev 1). Tests updated to assert that invariant plus
text-preservation, saved-revision cleanup, and append-after-compact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(6194): match the head<=1 post-compact contract

Tests previously asserted head=0 exactly after compaction; the
temp-pad-swap path lands at head=1 (one seed rev plus one content
rev) matching the shape of a freshly-imported pad. Relax the
assertions to  and derive the removed-count from
before-head minus after-head, so the tests still catch regressions in
text-preservation, saved-revision cleanup, and append-after-compact
without being tied to the exact implementation shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(6194): wrap existing Cleanup instead of duplicating it

Develop already ships a working revision-cleanup path under
`src/node/utils/Cleanup.ts` with two public helpers —
`deleteAllRevisions(padId)` (collapse full history via
copyPadWithoutHistory) and `deleteRevisions(padId, keepRevisions)`
(keep the last N). The admin-settings UI wires these up but neither
is exposed on the public API, and there's no CLI for operators who
want to run compaction outside the web UI. That's the gap this PR
now fills.

Changes from the prior revision of this PR:

- Drop `pad.compactHistory()` — it re-implemented what
  `Cleanup.deleteAllRevisions` already does. Remove the duplicate.
- `API.compactPad(padID, keepRevisions?)` now delegates to Cleanup:
    • keepRevisions null/undefined → deleteAllRevisions (full collapse)
    • keepRevisions >= 0          → deleteRevisions(N)  (keep last N)
  Returns {ok, mode: 'all' | 'keepLast', keepRevisions?}.
- APIHandler `1.3.1`: signature updated to take `keepRevisions`
  instead of `authorId`.
- `bin/compactPad.ts`: accepts `--keep N` for the keep-last mode,
  shows before/after revision counts so operators see concrete
  savings.
- Backend tests rewritten around the public API surface (mode
  reporting, text preservation, input validation) rather than
  internal method plumbing that no longer exists.

Net: strictly a thin public-API and CLI veneer over already-tested
Cleanup helpers. No new low-level logic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(6194): assert content markers, not byte-exact atext

Cleanup.deleteAllRevisions internally calls copyPadWithoutHistory
twice (src → tempId, tempId → src with force=true), and each round
trip normalizes trailing whitespace. That meant my byte-exact
atext.text assertion failed in CI:
  expected: '...line 3\n\n\n'
  actual:   '...line 3\n'

Swap the comparisons to use content markers (marker-alpha / beta /
gamma, keep-line-N). The test still catches the real regressions —
if compactPad lost content those markers would disappear — without
coupling to whitespace quirks of the existing Cleanup implementation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(6194): correct API param + document compactPad in http_api docs

The 1.3.1 entry in APIHandler registered `['padID', 'authorId']`, but
`API.compactPad` takes `(padID, keepRevisions)` and the CLI sends a
`keepRevisions` query param. APIHandler.handle dispatches by URL field
name, so the previous wiring silently dropped `keepRevisions` and never
ran the keep-last branch over HTTP.

- Register `['padID', 'keepRevisions']` so the handler forwards the
  CLI/HTTP arg into the API function.
- Add HTTP-level dispatch tests that hit `/api/1.3.1/compactPad` with
  and without `keepRevisions`. The direct `api.compactPad()` tests
  bypass the handler and would have missed this regression.
- Document compactPad in `doc/api/http_api.md` and `http_api.adoc`,
  and bump the documented latest version from 1.3.0 to 1.3.1 to match
  `latestApiVersion`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(6194): add bin/compactAllPads for per-instance bulk compaction

`bin/compactPad <padID>` covers the case where you know which pad is
fat. For "reclaim space across the whole instance," composing
`listAllPads` + `compactPad` yourself is annoying; this script does it.

- Walks every pad on the instance and compacts it (full collapse, or
  `--keep N` keep-last).
- Per-pad failures don't abort the run — they're logged, counted, and
  the script exits 1 if any failed.
- `--dry-run` lists pads + revision counts without writing anything,
  so operators can scope impact before committing.
- Reports `before → after` per pad and a total reclaimed count.

Deliberately not adding a `compactAllPads` HTTP API: bulk compaction
over a single HTTP request means one giant response and a long-held
connection. Operators who want this should run it locally, where they
can see progress and kill it cleanly. Staleness gating ("only pads
older than X days") is tracked separately as a follow-up.

Also registers `compactPad` and `compactAllPads` script aliases in
`bin/package.json` so they show up next to the other admin CLIs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(6194): cover the bin/compactAllPads loop logic

Previous commit added the script but only exercised it by hand. The
loop itself — error tolerance, dry-run gating, keep-last passthrough,
the empty-instance and listAllPads-failure paths — had no automated
coverage.

- Refactor compactAllPads.ts to export `runCompactAll(api, opts, logger)`
  and `parseArgs(argv)`. The CLI shell wires them up to axios+APIKEY
  for production; tests use an in-memory `CompactAllApi` so we don't
  need to stand up the apikey-auth path in mocha.
- Add 9 specs covering: arg parsing, full-collapse iteration,
  --keep N passthrough, --dry-run skipping writes, single-pad failure
  not aborting the run, pre-flight count failure tolerated, a
  listAllPads failure short-circuiting cleanly, the empty-instance
  no-op, and a final end-to-end test that runs `runCompactAll`
  against the real `/api/1.3.1/compactPad` handler over supertest+JWT
  to catch contract drift between the CompactAllApi shape and the
  HTTP endpoints.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(6194): address Qodo review — gate, integer check, SSL

Three valid concerns from the Qodo review on 75a08a13:

1. **cleanup.enabled gate.** The admin/Cleanup-socket path checks
   `settings.cleanup.enabled` before doing anything destructive; the
   public API was bypassing that gate. Now `compactPad` mirrors the
   admin path's check and returns a clear apierror when disabled, so
   exposing the API doesn't accidentally widen the cleanup-opt-in
   surface.

2. **Number.isFinite → Number.isInteger.** `2.5` was finite and
   non-negative, so the old check let it through into
   `Cleanup.deleteRevisions`, which does revision-index arithmetic
   that assumes integer math. Reject at the API boundary instead of
   silently misbehaving.

3. **SSL-aware baseURL in the bin scripts.** Other bin scripts
   hardcode `http://`, but the rest of the codebase uses
   `settings.ssl ? 'https' : 'http'`. The compact CLIs now do the
   same, so they work against HTTPS deployments. (Other bin scripts
   carry the same bug but fixing them is out of scope for this PR.)

Tests:
- New spec: `rejects fractional keepRevisions` (2.5 with the old
  check passed; the new one rejects).
- New spec: `refuses to run when cleanup.enabled is false`. The
  existing API tests opt in via a before-hook + restore, so they
  still cover the success path under the new gate.
- API docs (`http_api.md` + `http_api.adoc`) document the gate and
  the new error message.

Skipped Qodo concerns:
- "Wrong compactPad parameters" — already fixed in 26e12ff7
  (the param map now correctly says `keepRevisions`, not `authorId`).
- "Unbounded revision deletions" / "No session eviction" / changeset
  base-length / padCreate hook — these all targeted the earlier
  on-Pad implementation that was refactored away. The current code
  wraps `Cleanup.deleteAllRevisions` / `deleteRevisions`, which
  already handle concurrency, locking, and hook semantics.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 14:23:54 +01:00
John McLear
5e8704f8d8
feat(gdpr): pad deletion controls (PR1 of #6701) (#7546)
* docs: PR1 GDPR deletion-controls design spec

First of five GDPR PRs tracked in #6701. PR1 covers deletion controls:
one-time deletion token, allowPadDeletionByAllUsers flag, authorisation
matrix for handlePadDelete and the REST deletePad endpoint, a single
token-display modal for browser pad creators, and test coverage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: PR1 GDPR deletion-controls implementation plan

13 TDD-structured tasks covering PadDeletionManager unit tests, socket
+ REST three-way auth, clientVars wiring, one-time token modal,
delete-with-token UI, Playwright coverage, and PR handoff.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(gdpr): scaffolding for pad deletion tokens

PadDeletionManager stores a sha256-hashed per-pad deletion token and
verifies it with timing-safe comparison. createPad / createGroupPad
return the plaintext token once on first creation, and Pad.remove()
cleans it up. Gated behind the new allowPadDeletionByAllUsers flag
which defaults to false to preserve existing behaviour.

Part of #6701 (GDPR PR1).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix+test(gdpr): lazy DB access in PadDeletionManager + unit tests

Capturing DB.db at module-load time was null until DB.init() ran, which
broke importing the module outside a live server (including from the
test runner). Switch to DB.db.* at call time and add unit tests
exercising create/verify/remove plus timing-safe comparison.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(gdpr): three-way auth for socket PAD_DELETE

Creator cookie → valid deletion token → allowPadDeletionByAllUsers flag.
Anyone else still gets the existing refusal shout.

* feat(gdpr): optional deletionToken on programmatic deletePad

* feat(gdpr): advertise optional deletionToken on REST deletePad

* test(gdpr): cover deletePad authorisation matrix via REST

* feat(gdpr): surface padDeletionToken in clientVars for creators only

Revision-0 author on their first CLIENT_READY visit receives the
plaintext token; all subsequent CLIENT_READYs receive null because
createDeletionTokenIfAbsent is idempotent. Readonly sessions and any
other user never see the token.

* i18n(gdpr): strings for deletion-token modal and delete-with-token flow

* feat(gdpr): token modal + delete-with-token disclosure markup

* feat(gdpr): show deletion token once, allow delete via recovery token

* style(gdpr): modal + delete-with-token layout

* test(gdpr): Playwright coverage for deletion-token modal + delete-with-token

* fix(test): auto-dismiss deletion-token modal in goToNewPad helper

The token modal introduced in PR1 blocks clicks for every Playwright
test that creates a new pad via the shared helper. Add a one-line
dismissal so unrelated tests keep passing, and have the deletion-token
spec navigate inline via newPadKeepingModal() when it needs the modal
open to capture the token.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(test): dismiss deletion-token modal without focus transfer

Clicking the ack button transferred focus out of the pad iframe, which
made subsequent keyboard-driven tests (Tab / Enter) silently miss the
editor. Swap the click for a page.evaluate() that hides the modal and
nulls clientVars.padDeletionToken directly, leaving focus where it was.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(gdpr): PadDeletionManager race + document createPad/deletePad

Qodo review:
- createDeletionTokenIfAbsent() was a non-atomic read-then-write. Two
  concurrent callers for the same pad could both return different
  plaintext tokens while only the later hash was stored, leaving the
  first caller with an unusable recovery token. Serialise per-pad via a
  Promise chain and add a regression test that fires 8 concurrent
  calls and asserts exactly one plaintext is emitted and validates.
- doc/api/http_api.md now documents createPad returning deletionToken
  and deletePad accepting the optional deletionToken parameter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(gdpr): always render delete-with-token in settings popup

The rebase onto develop placed the delete-pad-with-token details inside
the pad-settings-section conditional, which is only rendered when
enablePadWideSettings is true AND the section is toggled visible.
Second-device recovery (typing the captured token on a fresh browser)
must work without pad-wide settings enabled, so move the details out
to sit alongside the existing pad_deletion_token.spec.ts expectations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(gdpr): require valid token when supplied, gate on auth, harden a11y/i18n

- PadMessageHandler: a supplied deletion token must validate; do not fall
  back to the creator-cookie path when the token is wrong (was deleting
  the pad anyway when the creator pasted a wrong token into the field).
- Skip token issuance + UI when requireAuthentication is on (creator
  identity is stable, recovery token is redundant noise).
- Server emits messageKey instead of hardcoded English; both shout
  handlers (inline alert and global gritter) localize via html10n.
- Suppress the global "Admin message" gritter for pad.deletionToken.*
  shouts to avoid the "Admin message: undefined" duplicate.
- Token-modal a11y: role=dialog, aria-modal, aria-labelledby/describedby,
  visually-hidden label on the token input, aria-live on Copy, focus to
  the token input on open and restore on dismiss.
- Style the "Delete Pad with Token" disclosure to match the Delete pad
  button; align the Copy/value row; pad the disclosure label.

Tests: Playwright now covers the creator-with-wrong-token path, asserts
no "Admin message" / "undefined" gritter on denial; backend API test
covers requireAuthentication suppressing the token.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 13:50:04 +01:00
Stefan Müller
8f16851373
Update versions in markdown files (#7465)
* Update versions in markdown files

* Update also adoc file
2026-04-12 12:27:03 +02:00
webzwo0i
8a76d2c680
chore: add docs how to obtain bearer token (#6328)
* chore: add docs how to obtain bearer token

* Added configurable ttl to settings

---------

Co-authored-by: SamTV12345 <40429738+samtv12345@users.noreply.github.com>
2024-04-29 17:04:00 +02:00
SamTV12345
d64924e9f5 Fixed http api grant type. 2024-04-20 11:19:31 +02:00
SamTV12345
8a66b04b68
chore: Added client credentials grant for API calling from services. (#6325)
* chore: Added client credentials grant for API calling from services.

* chore: Added authentication documentation
2024-04-13 10:32:23 +02:00
SamTV12345
d004d19dd7
Added vitepress for documentation. (#6270) 2024-03-23 20:58:05 +01:00
SamTV12345
dc0db68515
Added docs as asciidoctor with cross platform support. (#5733)
* Added docs as asciidoctor with cross platform support.

* Fixed release script with new doc building mechanism.
2023-06-21 13:13:31 +01:00
Robert Geislinger
58ec6a5c88 Update http_api.md
More information for public Staus HTTP API Calls
2022-02-24 19:45:36 -05:00
Robert Geislinger
4c53f76c25 Update http_api.md
The return Values of createGroupPad for success and error are not correct
2022-02-24 19:45:36 -05:00
Richard Hansen
aa286b7dbd API: Add optional authorId param to mutation functions 2022-02-19 14:55:42 -05:00
Robert Geislinger
10e2b09b96 Update http_api.md
The current version is 1.2.15 or bigger if you look at e.g. copyPadWithoutHistory
2021-12-14 01:16:38 -05:00
John McLear
0f16e518ff
api: drop JSONP (#4835)
* api: drop JSONP

* docs: drop JSONP

* tests: drop JSONP

* api: remove isValidJSONPName require
2021-02-22 09:10:02 +00:00
freddii
ea202e41f6 docs: fixed typos 2021-02-03 00:30:07 +01:00
John McLear
66df0a572f
Security: FEATURE REMOVAL: Remove all plain text password logic and ui (#4178)
This will be a breaking change for some people.  

We removed all internal password control logic.  If this affects you, you have two options:

1. Use a plugin for authentication and use session based pad access (recommended).
1. Use a plugin for password setting.

The reasoning for removing this feature is to reduce the overall security footprint of Etherpad.  It is unnecessary and cumbersome to keep this feature and with the thousands of available authentication methods available in the world our focus should be on supporting those and allowing more granual access based on their implementations (instead of half assed baking our own).
2020-10-07 13:43:54 +01:00
Joas Souza
8c04fe8775
Feature: Copy Pad without history (#4295)
New feature to copy a pad without copying entire history.  This is useful to perform a low CPU intensive operation while still copying current pad state.
2020-09-16 19:24:09 +01:00
Chocobozzz
82b919fc65 api: add getStats() function 2020-04-04 22:03:46 +02:00
Viljami Kuosmanen
e821bbcad8 openapi: add documentation, small optimisation 2020-04-03 01:03:11 +02:00
Ray Bellis
fc661ee13a core: allow URL parameters and POST bodies to co-exist.
Node 8.14.0 prohibits HTTP headers that exceed 8 KB (source:
https://nodejs.org/en/blog/vulnerability/november-2018-security-releases/#denial-of-service-with-large-http-headers-cve-2018-12121).

This patch allows for the parameters within the body of an HTTP POST request to
be used in addition to those within the URL (and will override them).

Closes #3568.

---
Muxator 2019-10-19:
- this commit was cherry-picked from 882b93487f
- it was modified to include the necessary changes in the documentation
2019-06-27 00:52:53 +02:00
muxator
705cc6f5e4 Change everywhere the link to https://etherpad.org (it was plain http) 2019-04-16 00:54:54 +02:00
HairyFotr
fce55df2b7 Fix typos 2019-01-16 11:14:04 +01:00
muxator
e9fb63f426 docs: "``js" -> "``json" in Markdown of some json blocks 2018-08-09 22:27:56 +02:00
HairyFotr
c7548450c0
Typos and minor fixes in bin, doc, and root 2017-09-14 13:33:27 +02:00
Alexander Lorz
cd7f01f40a minor changes in docs
- correct link to doc generation tool.
 - fixed incomplete sentence regarding API client libraries
2016-12-26 20:18:34 +01:00
Luc Didry
2341d09807 Add undocumented API function restoreRevision to doc
This commit is dedicated to Schoumi. Thx for supporting me on Tipeee :-)
2016-11-16 10:36:18 +01:00
Ted Mielczarek
c70d655b96 Add appendText API (from #2810) to docs. 2016-01-13 07:26:20 -05:00
Stefan
c8d7e6e0b8 Add appendChatMessage API to docs 2015-08-15 22:41:09 +02:00
John McLear
1d44490dc4 Merge pull request #2599 from Gared/doc_api_fix
Fix documentation for deleteSession api method
2015-04-11 15:07:18 +01:00
Stefan
a842eb4f5c Fix documentation for deleteSession api method 2015-04-11 16:05:41 +02:00
John McLear
84b3f1728f Merge pull request #2597 from Gared/increase_api_version
increase http api version to latest version
2015-04-11 14:56:47 +01:00
Stefan
d26c3d5f7e increase http api version 2015-04-11 13:45:59 +02:00
Stefan
601be46993 Add docs for new error message for createPad api method 2015-04-11 13:16:03 +02:00
Luc Didry
845788c39d Add a saveRevision API function
Calling saveRevision create an author which name is "API"
2015-02-25 01:04:27 +01:00
Luc Didry
a08c50a77d Fixes #1870
Add two functions to API :
 * getSavedRevisionsCount
 * listSavedRevisions
2015-02-24 23:42:35 +01:00
Luc Didry
4166f19078 Set correct API version in doc
The doc says current version is 1.2.9, but there is getPadID which uses 1.2.10 API.
2015-02-22 22:32:18 +01:00
Stephan Jauernick
bbd50dfc62 Added documentation for the newly added API method getPadID 2014-06-02 20:25:16 +02:00
Marcel Klehr
a7671a690b docs: update api version 2014-03-30 15:10:29 +02:00
Lennart Brinkmann
7dd29454e9 Remove duplicate doc entry for setHTML() 2013-12-14 21:11:37 +01:00
John McLear
58bbfd8a65 resolve merge conflict 2013-12-09 21:55:04 +00:00
s1341
5409cdaa3f add some documentation for move/copy 2013-11-18 14:38:44 +02:00
Luc Didry
b769acaee0 Forgot to bump the API version 2013-11-07 23:28:07 +01:00
Luc Didry
5bccfdf060 Forgot a small chunck of documentation for getRevisionChangeset 2013-11-07 22:46:35 +01:00
Luc Didry
a4194efaf1 Fixed #1981 - uncomplete HTTP API documentation 2013-11-07 22:22:37 +01:00
Eric Schrijver
b34224559d ‘Etherpad Lite’ -> ‘Etherpad’ 2013-09-29 13:57:37 +02:00
neurolit
e96baf6ef1 API documentation: listAllPads() returns {padIDs: [...]} instead of [...] 2013-03-26 16:37:57 +01:00
John McLear
9e1dfc9487 docs for setHTML 2013-02-12 23:44:09 +00:00
Manuel Knitza
fde10052ca Update doc/api/http_api.md
Updated
2013-02-12 21:57:01 +01:00
John McLear
a19ad983f1 docs for api change 2013-01-29 16:48:56 +00:00
mluto
cb2b93b133 Added getChatHistory and getChatHead (api+code+doc) 2013-01-26 14:35:26 +01:00
John McLear
654654b4e1 Merge pull request #1342 from spcsser/feature/padlisting
API & backend to list pads on epl instance
2013-01-14 08:13:57 -08:00