mirror of
https://github.com/ether/etherpad-lite.git
synced 2026-07-17 16:47:05 +00:00
* docs(updater): plan tier 4 — autonomous update in maintenance window (#7607) Maps PR 4 of the auto-update design spec (§"Tier 4 — autonomous") to concrete files, tasks, and verification steps. Subsequent commits scaffold against this plan. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): MaintenanceWindow module — wall-clock window math for tier 4 Pure module: parseWindow, inWindow, nextWindowStart. Supports tz=local|utc and cross-midnight ranges. Used by upcoming Scheduler + UpdatePolicy changes. 22 vitest unit tests cover format validation, same-day + cross-midnight boundaries, and host-local vs UTC clock comparisons. DST handling is absorbed by JS Date constructor's wall-clock normalization (documented in the file header). Refs #7607 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): tier 4 backend — window-gated UpdatePolicy + Scheduler Wires MaintenanceWindow into the existing tier 3 backend so autonomous updates only fire while `now` is inside `updates.maintenanceWindow`. UpdatePolicy - new optional `maintenanceWindow` input - canAutonomous flips on only for git+tier=autonomous+parse-valid window - new reasons `maintenance-window-missing` / `maintenance-window-invalid` - rollback-failed still wins over window denial Scheduler - decideSchedule snaps scheduledFor forward to nextWindowStart when canAutonomous + grace lands outside the window - decideTriggerApply returns a new `{action: 'defer'}` when canAutonomous + fire-time is outside the window; carries nextStart for the runner - canAutonomous=false preserves Tier 3 behavior unchanged index.ts wires settings.updates.maintenanceWindow through both passes and re-arms the timer on defer. Status endpoint surface (nextWindowOpensAt) + admin UI picker land in a follow-up commit. Settings adds `maintenanceWindow: {start, end, tz} | null`, defaulting to null. settings.json.template / settings.json.docker document the shape. Tests - 22 vitest cases for MaintenanceWindow already cover the math - 4 new UpdatePolicy cases for the window outcomes - 6 new Scheduler cases for tier-4 schedule/trigger paths - Full backend-new suite: 629 passed (35 files) Refs #7607 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): tier 4 admin UI — window status, deferred subtitle, banner GET /admin/update/status now returns: - `maintenanceWindow`: the parsed window object (admin sessions only) - `nextWindowOpensAt`: ISO of the next window opening when tier=autonomous UpdatePage - new "Maintenance window" section when tier=autonomous, shows current window summary + next opens at, or "Not configured" when unset - scheduled panel now appends a "deferred until <iso>" line when the backend has snapped scheduledFor to the next window opening UpdateBanner - new variant when tier=autonomous and policy.reason is `maintenance-window-missing` or `maintenance-window-invalid`, linking to /admin/update i18n - 8 new keys under `update.banner.*`, `update.page.policy.*`, `update.page.scheduled.*`, `update.window.*` (en.json only; translations follow via the usual locale workflow) Interactive picker is intentionally deferred — admins edit `updates.maintenanceWindow` via the parsed JSONC settings editor (#7709). A follow-up commit may add a thin write-through component if the JSONC round-trip turns out to be too rough for typical operators. Refs #7607 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(updater): tier 4 — window gate, DST notes, runbook §12 (#7607) CHANGELOG: flip Tier 4 from "designed, not yet implemented" to current. Document maintenanceWindow shape, snap-forward, defer-at-fire, and the two missing/invalid policy reasons. doc/admin/updates.md: new "Tier 4 — autonomous in a maintenance window" section with config example, policy gating, DST/timezone notes, admin UI behavior. runbook: §12 walks a disposable VM through missing-window, malformed, outside-window deferral, fire-at-opening, and window-closes-mid-grace. Adds five sign-off checklist items. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(updater): tier 4 window-boundary integration (#7607) Mocha integration covering the four scenarios called out in the spec §"Tier 4 — autonomous": - outside-window: decideSchedule snaps scheduledFor forward to the next opening and the snapped value round-trips through saveState - inside-window at fire-time: decideTriggerApply returns fire - window-closes-mid-grace: decideTriggerApply returns defer with nextStart at the next opening; persisted state moves forward - cancel during deferred-grace: state returns to idle, and the next decideSchedule pass re-emits a schedule snapped to the next opening All 4 cases passing locally under tsx mocha. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): real SMTP via nodemailer (mail.* settings) (#7607) Replaces the (would send email) stub introduced in PR #7601 with a nodemailer-backed transport. The dependency is lazy-imported so installs that don't set mail.host pay no runtime cost. Settings additions - new top-level mail block: host, port, secure, from, auth (user/pass) - mail.host=null keeps the legacy log-only behaviour; the Notifier still updates dedupe state so we don't re-evaluate every tick - settings.json.template documents the shape inline - settings.json.docker reads MAIL_HOST / MAIL_FROM / MAIL_PORT / MAIL_SECURE from env so operators can configure via container env Transport - lazy import('nodemailer') on first send - transport cached by host; settings reload picks up new host without needing a restart - send errors are swallowed (logged warn) so a transient SMTP failure can never poison the surrounding updater state machine - successful sends log at info; legacy "(would send email)" path remains the visible signal when mail is disabled Refs #7607 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): preflight checks target tag's engines.node (#7607) Before mutating the working tree, runPreflight now reads the target tag's package.json via `git show <tag>:package.json` and verifies that process.versions.node satisfies its engines.node range. Failures land at preflight-failed cleanly (no rollback needed — nothing has changed yet). Motivation: a release that bumps the Node floor used to either fail mid-`pnpm install` (which then rolls back successfully) or restart on the new build and crash in the boot path (which then rolls back via the health-check timer). Both paths recover, but they burn a drain + restart cycle on a condition we can reject upfront. Implementation - new PreflightReason `node-engine-mismatch` - new dep `readTargetEnginesNode(tag)` — runs the git-show as a child process with stdio captured to a string; missing tag / missing file / malformed JSON / missing engines.node all resolve to null (treated as "no constraint, pass") - uses existing semver dep with includePrerelease: true - new PreflightInput field `currentNodeVersion`; threaded from process.versions.node in both wirings (scheduler + manual apply) - check runs *after* signature verification so we trust the package.json - PreflightResult carries an optional `detail` string; applyPipeline appends it to the lastResult.reason so the admin UI shows e.g. "node-engine-mismatch: target requires Node >=26.0.0, running 25.0.0" Tests: 6 new vitest cases (no engines.node, satisfies, fails below floor, caret range, loose-spaced range, ordering after signature). Full backend-new: 635 passed (was 629). Refs #7607 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(updater): email admin on auto-rollback / preflight-failed (#7607) Before this commit, only the terminal rollback-failed state emailed the admin. Auto-recovered failures (rolled-back-install-failed, rolled-back- build-failed, rolled-back-health-check, rolled-back-crash-loop) and pre- flight-failed surfaced only via the /admin/update banner — so a 3am autonomous update that failed because of, say, a Node engine bump would roll back silently and stay invisible until the admin next logged in. Notifier - new EmailKinds: 'update-preflight-failed', 'update-rolled-back', 'update-rollback-failed' - new pure decideOutcomeEmail(input) → {toSend, newState} - dedupe key `<outcome>:<targetTag>` in EmailSendLog.lastFailureKey: same outcome on same tag emits one email per cycle (kills retry-loop spam); a different outcome or different tag resets the key - rollback-failed always fires (terminal — overrides dedupe) - state.ts validator + loadState backfill the new field for legacy state files (Tier 1/2/3 installs upgrading in place) Wiring - new index.ts helper notifyApplyFailure() loads state, runs the pure notifier, sends (via the nodemailer-backed sendEmailViaSmtp from the previous commit), persists the new dedupe key — all best-effort - schedulerTriggerApply: fires on applyUpdate returning preflight-failed or rolled-back - /admin/update/apply HTTP handler: same - boot path in expressCreateServer: if state.lastResult is a failure outcome we haven't already emailed about, fire then. Covers: - health-check timeout rollback (timer expired between boots) - crash-loop forced rollback caught on a later boot - preflight-failed where the process didn't get to email before exit - unacknowledged rollback-failed terminal Tests - 8 new vitest cases for decideOutcomeEmail (adminEmail=null, each outcome's content, dedupe by tag, dedupe by outcome, rollback-failed bypass) - Full backend-new suite: 643 passed (was 635) Refs #7607 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(updater): address Qodo review on tier 4 - UpdatePage: only show "deferred until" subtitle when scheduledFor actually matches nextWindowOpensAt. The previous `scheduledFor > now + 60s` heuristic misfired during a normal in-window 15-min grace period. - applyPipeline: return the enriched preflight reason (`reason: detail`) instead of only `pf.reason`, so /admin/update/apply 409 bodies and failure-notify emails preserve diagnostics like the Node engine mismatch detail. - updater/index: key the cached nodemailer transport on the full set of SMTP options (host + port + secure + auth) so runtime changes to port/credentials via reloadSettings() invalidate the cache. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
980 lines
34 KiB
Text
980 lines
34 KiB
Text
/*
|
|
* This file must be valid JSON. But comments are allowed
|
|
*
|
|
* Please edit settings.json, not settings.json.template
|
|
*
|
|
* Please note that starting from Etherpad 1.6.0 you can store DB credentials in
|
|
* a separate file (credentials.json).
|
|
*
|
|
*
|
|
* ENVIRONMENT VARIABLE SUBSTITUTION
|
|
* =================================
|
|
*
|
|
* All the configuration values can be read from environment variables using the
|
|
* syntax "${ENV_VAR}" or "${ENV_VAR:default_value}".
|
|
*
|
|
* This is useful, for example, when running in a Docker container.
|
|
*
|
|
* DETAILED RULES:
|
|
* - If the environment variable is set to the string "true" or "false", the
|
|
* value becomes Boolean true or false.
|
|
* - If the environment variable is set to the string "null", the value
|
|
* becomes null.
|
|
* - If the environment variable is set to the string "undefined", the setting
|
|
* is removed entirely, except when used as the member of an array in which
|
|
* case it becomes null.
|
|
* - If the environment variable is set to a string representation of a finite
|
|
* number, the string is converted to that number.
|
|
* - If the environment variable is set to any other string, including the
|
|
* empty string, the value is that string.
|
|
* - If the environment variable is unset and a default value is provided, the
|
|
* value is as if the environment variable was set to the provided default:
|
|
* - "${UNSET_VAR:}" becomes the empty string.
|
|
* - "${UNSET_VAR:foo}" becomes the string "foo".
|
|
* - "${UNSET_VAR:true}" and "${UNSET_VAR:false}" become true and false.
|
|
* - "${UNSET_VAR:null}" becomes null.
|
|
* - "${UNSET_VAR:undefined}" causes the setting to be removed (or be set
|
|
* to null, if used as a member of an array).
|
|
* - If the environment variable is unset and no default value is provided,
|
|
* the value becomes null. THIS BEHAVIOR MAY CHANGE IN A FUTURE VERSION OF
|
|
* ETHERPAD; if you want the default value to be null, you should explicitly
|
|
* specify "null" as the default value.
|
|
*
|
|
* EXAMPLE:
|
|
* "port": "${PORT:9001}"
|
|
* "minify": "${MINIFY}"
|
|
* "skinName": "${SKIN_NAME:colibris}"
|
|
*
|
|
* Would read the configuration values for those items from the environment
|
|
* variables PORT, MINIFY and SKIN_NAME.
|
|
*
|
|
* If PORT and SKIN_NAME variables were not defined, the default values 9001 and
|
|
* "colibris" would be used.
|
|
* The configuration value "minify", on the other hand, does not have a
|
|
* designated default value. Thus, if the environment variable MINIFY were
|
|
* undefined, "minify" would be null.
|
|
*
|
|
* REMARKS:
|
|
* 1) please note that variable substitution always needs to be quoted.
|
|
*
|
|
* "port": 9001, <-- Literal values. When not using
|
|
* "minify": false substitution, only strings must be
|
|
* "skinName": "colibris" quoted. Booleans and numbers must not.
|
|
*
|
|
* "port": "${PORT:9001}" <-- CORRECT: if you want to use a variable
|
|
* "minify": "${MINIFY:true}" substitution, put quotes around its name,
|
|
* "skinName": "${SKIN_NAME}" even if the required value is a number or
|
|
* a boolean.
|
|
* Etherpad will take care of rewriting it
|
|
* to the proper type if necessary.
|
|
*
|
|
* "port": ${PORT:9001} <-- ERROR: this is not valid json. Quotes
|
|
* "minify": ${MINIFY} around variable names are missing.
|
|
* "skinName": ${SKIN_NAME}
|
|
*
|
|
* 2) Beware of undefined variables and default values: nulls and empty strings
|
|
* are different!
|
|
*
|
|
* This is particularly important for user's passwords (see the relevant
|
|
* section):
|
|
*
|
|
* "password": "${PASSW}" // if PASSW is not defined would result in password === null
|
|
* "password": "${PASSW:}" // if PASSW is not defined would result in password === ''
|
|
*
|
|
* If you want to use an empty value (null) as default value for a variable,
|
|
* simply do not set it, without putting any colons: "${SOFFICE}".
|
|
*
|
|
* 3) if you want to use newlines in the default value of a string parameter,
|
|
* use "\n" as usual.
|
|
*
|
|
* "defaultPadText" : "${DEFAULT_PAD_TEXT:Line 1\nLine 2}"
|
|
*/
|
|
{
|
|
/*
|
|
* Name your instance!
|
|
*/
|
|
"title": "Etherpad",
|
|
|
|
/*
|
|
* Whether to show recent pads on the homepage or not.
|
|
*/
|
|
"showRecentPads": true,
|
|
|
|
/*
|
|
* Pathname of the favicon you want to use. If null, the skin's favicon is
|
|
* used if one is provided by the skin, otherwise the default Etherpad favicon
|
|
* is used. If this is a relative path it is interpreted as relative to the
|
|
* Etherpad root directory.
|
|
*/
|
|
"favicon": null,
|
|
|
|
/*
|
|
* Canonical public origin of this Etherpad instance, e.g.
|
|
* "https://pad.example.com" (no trailing slash, must include scheme).
|
|
*
|
|
* When set, this is used to build absolute URLs in server-rendered output
|
|
* such as the Open Graph / Twitter Card link-preview meta tags (og:url,
|
|
* og:image, ...). When null, those URLs fall back to the request's
|
|
* protocol+Host, which can reflect client-controlled headers if your
|
|
* reverse proxy passes them through unsanitized.
|
|
*
|
|
* Set this in production deployments to lock down the canonical origin
|
|
* advertised in shared link previews.
|
|
*/
|
|
"publicURL": null,
|
|
|
|
/*
|
|
* Open Graph / Twitter Card metadata, served on the homepage, pad pages and
|
|
* timeslider for nicer previews when a pad URL is shared in chat apps
|
|
* (WhatsApp, Signal, Slack, ...).
|
|
*
|
|
* - description: when set to a non-empty string, this exact text is used as
|
|
* og:description / twitter:description regardless of the visitor's
|
|
* negotiated language. Most link-preview crawlers don't send an
|
|
* Accept-Language header, so without an override they always see the
|
|
* English fallback. Set this if your instance serves a non-English
|
|
* audience and you want a fixed blurb in shared previews.
|
|
*
|
|
* Leave description as null to use Etherpad's i18n catalog (key
|
|
* `pad.social.description`), which honours Accept-Language and can be
|
|
* overridden per-language via `customLocaleStrings` further down.
|
|
*/
|
|
"socialMeta": {
|
|
"description": null
|
|
},
|
|
|
|
/*
|
|
* Skin name.
|
|
*
|
|
* Its value has to be an existing directory under src/static/skins.
|
|
* You can write your own, or use one of the included ones:
|
|
*
|
|
* - "no-skin": an empty skin (default). This yields the unmodified,
|
|
* traditional Etherpad theme.
|
|
* - "colibris": the new experimental skin (since Etherpad 1.8), candidate to
|
|
* become the default in Etherpad 2.0
|
|
*/
|
|
"skinName": "colibris",
|
|
|
|
/*
|
|
* Skin Variants
|
|
*
|
|
* Use the UI skin variants builder at /p/test#skinvariantsbuilder
|
|
*
|
|
* For the colibris skin only, you can choose how to render the three main
|
|
* containers:
|
|
* - toolbar (top menu with icons)
|
|
* - editor (containing the text of the pad)
|
|
* - background (area outside of editor, mostly visible when using page style)
|
|
*
|
|
* For each of the 3 containers you can choose 4 color combinations:
|
|
* super-light, light, dark, super-dark.
|
|
*
|
|
* For example, to make the toolbar dark, you will include "dark-toolbar" into
|
|
* skinVariants.
|
|
*
|
|
* You can provide multiple skin variants separated by spaces. Default
|
|
* skinVariant is "super-light-toolbar super-light-editor light-background".
|
|
*
|
|
* For the editor container, you can also make it full width by adding
|
|
* "full-width-editor" variant (by default editor is rendered as a page, with
|
|
* a max-width of 900px).
|
|
*/
|
|
"skinVariants": "super-light-toolbar super-light-editor light-background",
|
|
|
|
/*
|
|
* IP and port which Etherpad should bind at.
|
|
*
|
|
* Binding to a Unix socket is also supported: just use an empty string for
|
|
* the ip, and put the full path to the socket in the port parameter.
|
|
*
|
|
* EXAMPLE USING UNIX SOCKET:
|
|
* "ip": "", // <-- has to be an empty string
|
|
* "port" : "/somepath/etherpad.socket", // <-- path to a Unix socket
|
|
*/
|
|
"ip": "0.0.0.0",
|
|
"port": 9001,
|
|
|
|
/*
|
|
* Option to hide/show the settings.json in admin page.
|
|
*
|
|
* Default option is set to true
|
|
*/
|
|
"showSettingsInAdminPage": true,
|
|
|
|
/*
|
|
* Enable/disable the metrics endpoint.
|
|
*
|
|
* This is used by the monitoring plugins to collect metrics about Etherpad.
|
|
* If you do not use any monitoring plugins, you can disable this.
|
|
*/
|
|
"enableMetrics": "${ENABLE_METRICS:true}",
|
|
|
|
/*
|
|
* Self-update subsystem.
|
|
* tier: "off" | "notify" | "manual" | "auto" | "autonomous"
|
|
* Default "notify" shows a banner when an update is available. "off" disables the version check.
|
|
*/
|
|
"updates": {
|
|
"tier": "notify",
|
|
"source": "github",
|
|
"channel": "stable",
|
|
"installMethod": "auto",
|
|
"checkIntervalHours": 6,
|
|
"githubRepo": "ether/etherpad",
|
|
/*
|
|
* Lock /admin/update/status to authenticated admins. Default false keeps the
|
|
* endpoint open (the version is already public via /health). Set true to hide
|
|
* full update detail from non-admins without turning the updater off.
|
|
*/
|
|
"requireAdminForStatus": false,
|
|
/*
|
|
* Tier 2+ knobs. Only meaningful at tier "manual" or higher.
|
|
* - preApplyGraceMinutes: tier 3 only — grace minutes between detecting a new
|
|
* release and starting the drain. Clamped to [0, 7*24*60] (one week).
|
|
* Default 0 applies immediately when allowed; raise it to give admins
|
|
* time to cancel via /admin/update.
|
|
* - drainSeconds: how long to broadcast "restart imminent" before exiting.
|
|
* - rollbackHealthCheckSeconds: window after restart for /health to come up.
|
|
* - diskSpaceMinMB: pre-flight refuses to start an update without this much free.
|
|
* - requireSignature: refuse updates whose tag isn't signed by a trusted key.
|
|
* - trustedKeysPath: override the keyring location passed to git verify-tag (GNUPGHOME).
|
|
* - maintenanceWindow: tier 4 only — nightly window during which the scheduler
|
|
* may fire. Null = tier 4 disabled (with tier="autonomous", the policy
|
|
* downgrades to canAuto). Shape: {"start":"HH:MM","end":"HH:MM","tz":"local"|"utc"}.
|
|
* `end` is exclusive; `end < start` denotes a cross-midnight window.
|
|
*/
|
|
"preApplyGraceMinutes": 0,
|
|
"drainSeconds": 60,
|
|
"rollbackHealthCheckSeconds": 60,
|
|
"diskSpaceMinMB": 500,
|
|
"requireSignature": false,
|
|
"trustedKeysPath": null,
|
|
"maintenanceWindow": null
|
|
},
|
|
|
|
/*
|
|
* Admin OpenAPI document at /admin/openapi.json.
|
|
*
|
|
* Disabled by default per Etherpad's "new features behind a flag, off by
|
|
* default" policy. The admin UI's typed client embeds the spec at build
|
|
* time, so the runtime route is only useful for third-party tooling
|
|
* (Postman, swagger-ui, downstream clients). Set `enabled: true` to mount
|
|
* the route; the SPA at /admin/ continues to serve normally either way.
|
|
*/
|
|
"adminOpenAPI": {
|
|
"enabled": false
|
|
},
|
|
|
|
/*
|
|
* Contact address for admin notifications (updates, security advisories, future features).
|
|
* Set to null to disable outbound mail from the updater.
|
|
*/
|
|
"adminEmail": null,
|
|
|
|
/*
|
|
* SMTP transport for outbound admin notifications. host=null keeps the
|
|
* legacy log-only behaviour (Notifier still dedupes; nothing leaves the
|
|
* box). Set host+from (and optionally auth) to deliver via nodemailer.
|
|
* The dependency is lazy-loaded so installs without mail.host pay no
|
|
* runtime cost.
|
|
*
|
|
* "mail": {
|
|
* "host": "smtp.example.com",
|
|
* "port": 587,
|
|
* "secure": false,
|
|
* "from": "etherpad@example.com",
|
|
* "auth": { "user": "smtp-user", "pass": "smtp-pass" }
|
|
* }
|
|
*/
|
|
"mail": {
|
|
"host": null,
|
|
"port": 587,
|
|
"secure": false,
|
|
"from": null,
|
|
"auth": null
|
|
},
|
|
|
|
/*
|
|
* Settings for cleanup of pads
|
|
*/
|
|
"cleanup": {
|
|
"enabled": false,
|
|
"keepRevisions": 5
|
|
},
|
|
|
|
/*
|
|
* GDPR Art. 17 author erasure REST endpoint (anonymizeAuthor).
|
|
*
|
|
* Disabled by default — enable only when an operator process exists to
|
|
* authorise erasure requests. While disabled, calls to anonymizeAuthor
|
|
* return an apierror and the AuthorManager helper is not exposed via
|
|
* the public API.
|
|
*/
|
|
"gdprAuthorErasure": {
|
|
"enabled": false
|
|
},
|
|
|
|
/*
|
|
* Node native SSL support
|
|
*
|
|
* This is disabled by default.
|
|
* Make sure to have the minimum and correct file access permissions set so
|
|
* that the Etherpad server can access them
|
|
*/
|
|
|
|
/*
|
|
"ssl" : {
|
|
"key" : "/path-to-your/epl-server.key",
|
|
"cert" : "/path-to-your/epl-server.crt",
|
|
"ca": ["/path-to-your/epl-intermediate-cert1.crt", "/path-to-your/epl-intermediate-cert2.crt"]
|
|
},
|
|
*/
|
|
|
|
/*
|
|
* The type of the database.
|
|
*
|
|
* You can choose between many DB drivers, for example: dirty, postgres,
|
|
* sqlite, mysql.
|
|
*
|
|
* You shouldn't use "dirty" for for anything else than testing or
|
|
* development.
|
|
*
|
|
*
|
|
* Database specific settings are dependent on dbType, and go in dbSettings.
|
|
* Remember that since Etherpad 1.6.0 you can also store this information in
|
|
* credentials.json.
|
|
*
|
|
* For a complete list of the supported drivers, please refer to:
|
|
* https://www.npmjs.com/package/ueberdb2
|
|
*/
|
|
|
|
"dbType": "dirty",
|
|
"dbSettings": {
|
|
"filename": "var/dirty.db"
|
|
},
|
|
|
|
/*
|
|
* An Example of MySQL Configuration (commented out).
|
|
*
|
|
* See: https://github.com/ether/etherpad/wiki/How-to-use-Etherpad-Lite-with-MySQL
|
|
*/
|
|
|
|
/*
|
|
"dbType" : "mysql",
|
|
"dbSettings" : {
|
|
"user": "etherpaduser",
|
|
"host": "localhost",
|
|
"port": 3306,
|
|
"password": "PASSWORD",
|
|
"database": "etherpad_lite_db",
|
|
"charset": "utf8mb4"
|
|
},
|
|
*/
|
|
|
|
/*
|
|
* The default text of a pad
|
|
*/
|
|
"defaultPadText" : "Welcome to Etherpad!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nGet involved with Etherpad at https:\/\/etherpad.org\n",
|
|
|
|
/*
|
|
* Default Pad behavior.
|
|
*
|
|
* Change them if you want to override.
|
|
*/
|
|
"padOptions": {
|
|
"noColors": false,
|
|
"showControls": true,
|
|
"showChat": true,
|
|
"showLineNumbers": true,
|
|
"useMonospaceFont": false,
|
|
"userName": null,
|
|
"userColor": null,
|
|
"rtl": false,
|
|
"alwaysShowChat": false,
|
|
"chatAndUsers": false,
|
|
"lang": null,
|
|
/*
|
|
* When true (default), each author's caret/background color fades toward white
|
|
* as the author goes inactive. Set to false if users pick light colors and the
|
|
* faded variants become visually indistinguishable.
|
|
*/
|
|
"fadeInactiveAuthorColors": true,
|
|
/*
|
|
* Clamp author background colors to a WCAG 2.1 AA contrast ratio (4.5:1)
|
|
* against the rendered text colour at render time. The author's stored
|
|
* colour is not modified — only the displayed shade is adjusted. Set to
|
|
* false to render exact author colours regardless of contrast.
|
|
*/
|
|
"enforceReadableAuthorColors": true
|
|
},
|
|
|
|
/*
|
|
* Pad Shortcut Keys
|
|
*/
|
|
"padShortcutEnabled" : {
|
|
"altF9": true, /* focus on the File Menu and/or editbar */
|
|
"altC": true, /* focus on the Chat window */
|
|
"cmdShift2": true, /* shows a gritter popup showing a line author */
|
|
"delete": true,
|
|
"return": true,
|
|
"esc": true, /* in mozilla versions 14-19 avoid reconnecting pad */
|
|
"cmdS": true, /* save a revision */
|
|
"tab": true, /* indent */
|
|
"cmdZ": true, /* undo/redo */
|
|
"cmdY": true, /* redo */
|
|
"cmdI": true, /* italic */
|
|
"cmdB": true, /* bold */
|
|
"cmdU": true, /* underline */
|
|
"cmd5": true, /* strike through */
|
|
"cmdShiftL": true, /* unordered list */
|
|
"cmdShiftN": true, /* ordered list */
|
|
"cmdShift1": true, /* ordered list */
|
|
"cmdShiftC": true, /* clear authorship */
|
|
"cmdH": true, /* backspace */
|
|
"ctrlHome": true, /* scroll to top of pad */
|
|
"pageUp": true,
|
|
"pageDown": true
|
|
},
|
|
|
|
/*
|
|
* Enables the use of a different server. We have a different one that syncs changes from the original server.
|
|
* It is hosted on GitHub and should not be blocked by many firewalls.
|
|
* https://etherpad.org/ep_infos
|
|
*/
|
|
|
|
"updateServer": "https://etherpad.org/ep_infos",
|
|
|
|
/*
|
|
* Outbound network calls. See PRIVACY.md for what each one sends.
|
|
* - updateCheck=false : disables hourly version check (UpdateCheck.ts)
|
|
* - pluginCatalog=false: disables admin plugin browser
|
|
* (manual install-by-name via CLI still works)
|
|
*/
|
|
"privacy": {
|
|
"updateCheck": true,
|
|
"pluginCatalog": true
|
|
},
|
|
|
|
/*
|
|
* Should we suppress errors from being visible in the default Pad Text?
|
|
*/
|
|
"suppressErrorsInPadText": false,
|
|
|
|
/*
|
|
* If this option is enabled, a user must have a session to access pads.
|
|
* This effectively allows only group pads to be accessed.
|
|
*/
|
|
"requireSession": false,
|
|
|
|
/*
|
|
* Users may edit pads but not create new ones.
|
|
*
|
|
* Pad creation is only via the API.
|
|
* This applies both to group pads and regular pads.
|
|
*/
|
|
"editOnly": false,
|
|
|
|
/*
|
|
* If true, all css & js will be minified before sending to the client.
|
|
*
|
|
* This will improve the loading performance massively, but makes it difficult
|
|
* to debug the javascript/css
|
|
*/
|
|
"minify": true,
|
|
|
|
/*
|
|
* How long may clients use served javascript code (in seconds)?
|
|
*
|
|
* Not setting this may cause problems during deployment.
|
|
* Set to 0 to disable caching.
|
|
*/
|
|
"maxAge": 21600, // 60 * 60 * 6 = 6 hours
|
|
|
|
/*
|
|
* Absolute path to the soffice (LibreOffice) executable.
|
|
*
|
|
* When configured, soffice handles all advanced office-format
|
|
* conversions (docx, pdf, odt, doc, rtf — both directions). When
|
|
* null, Etherpad falls back to pure-JS in-process converters
|
|
* that handle docx export, pdf export, and docx import natively.
|
|
* odt/doc/rtf export and pdf import still require soffice.
|
|
*/
|
|
"soffice": null,
|
|
|
|
/*
|
|
* When true (the default), the "Microsoft Word" export button
|
|
* downloads a .docx file -- via soffice if it's configured, or via
|
|
* the in-process html-to-docx converter otherwise. Set to false to
|
|
* revert to legacy .doc output (.doc still requires soffice).
|
|
*/
|
|
"docxExport": true,
|
|
|
|
/*
|
|
* txt, doc, docx, rtf, odt, html & htm
|
|
*/
|
|
"allowUnknownFileEnds": true,
|
|
|
|
/*
|
|
* This setting is used if you require authentication of all users.
|
|
*
|
|
* Note: "/admin" always requires authentication.
|
|
*/
|
|
"requireAuthentication": false,
|
|
|
|
/*
|
|
* Require authorization by a module, or a user with is_admin set, see below.
|
|
*/
|
|
"requireAuthorization": false,
|
|
|
|
/*
|
|
* When you use NGINX or another proxy/load-balancer set this to true.
|
|
*
|
|
* This is especially necessary when the reverse proxy performs SSL
|
|
* termination, otherwise the cookies will not have the "secure" flag.
|
|
*
|
|
* The other effect will be that the logs will contain the real client's IP,
|
|
* instead of the reverse proxy's IP.
|
|
*/
|
|
"trustProxy": false,
|
|
|
|
/*
|
|
* Settings controlling the session cookie issued by Etherpad.
|
|
*/
|
|
"cookie": {
|
|
/*
|
|
* Prefix for all cookie names set by Etherpad. Set this to "ep_" or similar
|
|
* if Etherpad's cookie names (token, sessionID, etc.) conflict with those
|
|
* of another application on the same domain. Default: "" (no prefix).
|
|
*/
|
|
// "prefix": "ep_",
|
|
|
|
/*
|
|
* How often (in milliseconds) the key used to sign the express_sid cookie
|
|
* should be rotated. Long rotation intervals reduce signature verification
|
|
* overhead (because there are fewer historical keys to check) and database
|
|
* load (fewer historical keys to store, and less frequent queries to
|
|
* get/update the keys). Short rotation intervals are slightly more secure.
|
|
*
|
|
* Multiple Etherpad processes sharing the same database (table) is
|
|
* supported as long as the clock sync error is significantly less than this
|
|
* value.
|
|
*
|
|
* Key rotation can be disabled (not recommended) by setting this to 0 or
|
|
* null, or by disabling session expiration (see sessionLifetime).
|
|
*/
|
|
"keyRotationInterval": 86400000, // = 1d * 24h/d * 60m/h * 60s/m * 1000ms/s
|
|
|
|
/*
|
|
* Value of the SameSite cookie property. "Lax" is recommended unless
|
|
* Etherpad will be embedded in an iframe from another site, in which case
|
|
* this must be set to "None". Note: "None" will not work (the browser will
|
|
* not send the cookie to Etherpad) unless https is used to access Etherpad
|
|
* (either directly or via a reverse proxy with "trustProxy" set to true).
|
|
*
|
|
* "Strict" is not recommended because it has few security benefits but
|
|
* significant usability drawbacks vs. "Lax". See
|
|
* https://stackoverflow.com/q/41841880 for discussion.
|
|
*/
|
|
"sameSite": "Lax",
|
|
|
|
/*
|
|
* How long (in milliseconds) after navigating away from Etherpad before the
|
|
* user is required to log in again. (The express_sid cookie is set to
|
|
* expire at time now + sessionLifetime when first created, and its
|
|
* expiration time is periodically refreshed to a new now + sessionLifetime
|
|
* value.) If requireAuthentication is false then this value does not really
|
|
* matter.
|
|
*
|
|
* The "best" value depends on your users' usage patterns and the amount of
|
|
* convenience you desire. A long lifetime is more convenient (users won't
|
|
* have to log back in as often) but has some drawbacks:
|
|
* - It increases the amount of state kept in the database.
|
|
* - It might weaken security somewhat: The cookie expiration is refreshed
|
|
* indefinitely without consulting authentication or authorization
|
|
* hooks, so once a user has accessed a pad, the user can continue to
|
|
* use the pad until the user leaves for longer than sessionLifetime.
|
|
* - More historical keys (sessionLifetime / keyRotationInterval) must be
|
|
* checked when verifying signatures.
|
|
*
|
|
* Session lifetime can be set to infinity (not recommended) by setting this
|
|
* to null or 0. Note that if the session does not expire, most browsers
|
|
* will delete the cookie when the browser exits, but a session record is
|
|
* kept in the database forever.
|
|
*/
|
|
"sessionLifetime": 864000000, // = 10d * 24h/d * 60m/h * 60s/m * 1000ms/s
|
|
|
|
/*
|
|
* How long (in milliseconds) before the expiration time of an active user's
|
|
* session is refreshed (to now + sessionLifetime). This setting affects the
|
|
* following:
|
|
* - How often a new session expiration time will be written to the
|
|
* database.
|
|
* - How often each user's browser will ping the Etherpad server to
|
|
* refresh the expiration time of the session cookie.
|
|
*
|
|
* High values reduce the load on the database and the load from browsers,
|
|
* but can shorten the effective session lifetime if Etherpad is restarted
|
|
* or the user navigates away.
|
|
*
|
|
* Automatic session refreshes can be disabled (not recommended) by setting
|
|
* this to null.
|
|
*/
|
|
"sessionRefreshInterval": 86400000, // = 1d * 24h/d * 60m/h * 60s/m * 1000ms/s
|
|
|
|
/*
|
|
* Whether to periodically clean up expired and stale sessions from the
|
|
* database. Set to false to disable. Default: true.
|
|
*/
|
|
"sessionCleanup": true
|
|
},
|
|
|
|
/*
|
|
* Controls what Etherpad writes to its logs about client IP addresses.
|
|
*
|
|
* "anonymous" — replace every IP with the literal "ANONYMOUS" (default)
|
|
* "truncated" — zero the last octet of IPv4 (1.2.3.0); truncate IPv6 to
|
|
* the first /48 (2001:db8:1::). Keeps aggregate visibility.
|
|
* "full" — log the full IP (document a legal basis + retention
|
|
* policy before choosing this).
|
|
*
|
|
* In-memory rate-limiting always keys on the raw IP and is never persisted.
|
|
*/
|
|
"ipLogging": "anonymous",
|
|
|
|
/*
|
|
* Deprecated — use `ipLogging` above instead. Still honoured for one release
|
|
* cycle: `true` maps to `ipLogging: "anonymous"`, `false` maps to `"full"`.
|
|
* A deprecation warning is emitted when only this legacy setting is present.
|
|
*/
|
|
"disableIPlogging": false,
|
|
|
|
/*
|
|
* Allow any user who can edit a pad to delete it without the one-time pad
|
|
* deletion token. If false, only the original creator's author cookie or the
|
|
* deletion token can delete the pad.
|
|
*/
|
|
"allowPadDeletionByAllUsers": false,
|
|
|
|
/*
|
|
* Time (in seconds) to automatically reconnect pad when a "Force reconnect"
|
|
* message is shown to user.
|
|
*
|
|
* Set to 0 to disable automatic reconnection.
|
|
*/
|
|
"automaticReconnectionTimeout": 0,
|
|
|
|
/*
|
|
* By default, when caret is moved out of viewport, it scrolls the minimum
|
|
* height needed to make this line visible.
|
|
*/
|
|
"scrollWhenFocusLineIsOutOfViewport": {
|
|
|
|
/*
|
|
* Percentage of viewport height to be additionally scrolled.
|
|
*
|
|
* E.g.: use "percentage.editionAboveViewport": 0.5, to place caret line in
|
|
* the middle of viewport, when user edits a line above of the
|
|
* viewport
|
|
*
|
|
* Set to 0 to disable extra scrolling
|
|
*/
|
|
"percentage": {
|
|
"editionAboveViewport": 0,
|
|
"editionBelowViewport": 0
|
|
},
|
|
|
|
/*
|
|
* Time (in milliseconds) used to animate the scroll transition.
|
|
* Set to 0 to disable animation
|
|
*/
|
|
"duration": 0,
|
|
|
|
/*
|
|
* Flag to control if it should scroll when user places the caret in the
|
|
* last line of the viewport
|
|
*/
|
|
"scrollWhenCaretIsInTheLastLineOfViewport": false,
|
|
|
|
/*
|
|
* Percentage of viewport height to be additionally scrolled when user
|
|
* presses arrow up in the line of the top of the viewport.
|
|
*
|
|
* Set to 0 to let the scroll to be handled as default by Etherpad
|
|
*/
|
|
"percentageToScrollWhenUserPressesArrowUp": 0
|
|
},
|
|
|
|
/*
|
|
* User accounts. These accounts are used by:
|
|
* - default HTTP basic authentication if no plugin handles authentication
|
|
* - some but not all authentication plugins
|
|
* - some but not all authorization plugins
|
|
*
|
|
* User properties:
|
|
* - password: The user's password. Some authentication plugins will ignore
|
|
* this.
|
|
* - is_admin: true gives access to /admin. Defaults to false. If you do not
|
|
* uncomment this, /admin will not be available!
|
|
* - readOnly: If true, this user will not be able to create new pads or
|
|
* modify existing pads. Defaults to false.
|
|
* - canCreate: If this is true and readOnly is false, this user can create
|
|
* new pads. Defaults to true.
|
|
*
|
|
* Authentication and authorization plugins may define additional properties.
|
|
*
|
|
* WARNING: passwords should not be stored in plaintext in this file.
|
|
* If you want to mitigate this, please install ep_hash_auth and
|
|
* follow the section "secure your installation" in README.md
|
|
*/
|
|
|
|
/*
|
|
"users": {
|
|
"admin": {
|
|
// 1) "password" can be replaced with "hash" if you install ep_hash_auth
|
|
// 2) please note that if password is null, the user will not be created
|
|
"password": "changeme1",
|
|
"is_admin": true
|
|
},
|
|
"user": {
|
|
// 1) "password" can be replaced with "hash" if you install ep_hash_auth
|
|
// 2) please note that if password is null, the user will not be created
|
|
"password": "changeme1",
|
|
"is_admin": false
|
|
}
|
|
},
|
|
*/
|
|
|
|
/*
|
|
* Restrict socket.io transport methods
|
|
*/
|
|
"socketTransportProtocols" : ["websocket", "polling"],
|
|
|
|
"socketIo": {
|
|
/*
|
|
* Maximum permitted client message size (in bytes). This controls the
|
|
* maximum single-message size for socket.io and directly affects large
|
|
* paste operations. All messages from clients that are larger than this
|
|
* will be rejected. Large values make it possible to paste large amounts
|
|
* of text, and plugins may require a larger value to work properly, but
|
|
* increasing the value increases susceptibility to denial of service
|
|
* attacks (malicious clients can exhaust memory).
|
|
*
|
|
* 1MB accommodates large pastes while still preventing abuse.
|
|
*/
|
|
"maxHttpBufferSize": 1000000
|
|
},
|
|
|
|
/*
|
|
* Allow Load Testing tools to hit the Etherpad Instance.
|
|
*
|
|
* WARNING: this will disable security on the instance.
|
|
*/
|
|
"loadTest": false,
|
|
|
|
/**
|
|
* Disable dump of objects preventing a clean exit
|
|
*/
|
|
"dumpOnUncleanExit": false,
|
|
|
|
/*
|
|
* Disable indentation on new line when previous line ends with some special
|
|
* chars (':', '[', '(', '{')
|
|
*/
|
|
|
|
/*
|
|
"indentationOnNewLine": false,
|
|
*/
|
|
|
|
/*
|
|
* From Etherpad 1.8.3 onwards, import and export of pads is always rate
|
|
* limited.
|
|
*
|
|
* The default is to allow at most 10 requests per IP in a 90 seconds window.
|
|
* After that the import/export request is rejected.
|
|
*
|
|
* See https://github.com/nfriedly/express-rate-limit for more options
|
|
*/
|
|
"importExportRateLimiting": {
|
|
// duration of the rate limit window (milliseconds)
|
|
"windowMs": 90000,
|
|
|
|
// maximum number of requests per IP to allow during the rate limit window
|
|
"max": 10
|
|
},
|
|
|
|
/*
|
|
* From Etherpad 1.8.3 onwards, the maximum allowed size for a single imported
|
|
* file is always bounded.
|
|
*
|
|
* File size is specified in bytes. Default is 50 MB.
|
|
*/
|
|
"importMaxFileSize": 52428800, // 50 * 1024 * 1024
|
|
|
|
/*
|
|
The authentication method used by the server.
|
|
The default value is sso
|
|
If you want to use the old authentication system, change this to apikey
|
|
*/
|
|
"authenticationMethod": "${AUTHENTICATION_METHOD:sso}",
|
|
|
|
/**
|
|
* Allow setting dark mode for the enduser. This is so if the user has preferred dark mode in their browser, Etherpad will respect that.
|
|
* Of course this overrides all the skin variants and the skinName set by the administrator.
|
|
**/
|
|
"enableDarkMode": "${ENABLE_DARK_MODE:true}",
|
|
|
|
/**
|
|
* Enable creator-owned Pad-wide Settings and new-pad default seeding from My View.
|
|
* The pad creator (revision-0 author) gets the "Pad-wide Settings" section,
|
|
* which lets them set defaults and optionally enforce them for other users.
|
|
* Other users see only "User Settings" (their own view options).
|
|
* Set to false to revert to the legacy single-settings behavior.
|
|
**/
|
|
"enablePadWideSettings": "${ENABLE_PAD_WIDE_SETTINGS:true}",
|
|
|
|
/*
|
|
* Allow plugins to ride the existing padoptions COLLABROOM rail by
|
|
* accepting pad-wide values under plugin-namespaced keys matching
|
|
* /^ep_[a-z0-9_]+$/ (e.g. ep_table_of_contents). Values are validated
|
|
* (JSON-safe, 64 KB per key, 256 KB total) and broadcast to every
|
|
* connected client just like native pad-wide toggles. Disabled by
|
|
* default; flip to true once your plugins (e.g. ep_plugin_helpers'
|
|
* padToggle) require it. See doc/plugins.md.
|
|
**/
|
|
"enablePluginPadOptions": "${ENABLE_PLUGIN_PAD_OPTIONS:false}",
|
|
|
|
/*
|
|
* Optional privacy banner shown once the pad loads. Disabled by default.
|
|
*
|
|
* enabled — toggle the feature
|
|
* title — plain-text heading (HTML is escaped)
|
|
* body — plain-text body; newlines become paragraph breaks
|
|
* learnMoreUrl — optional URL rendered as a "Learn more" link
|
|
* dismissal — "dismissible" (close button, stored in localStorage)
|
|
* or "sticky" (always shown, no close button)
|
|
*/
|
|
"privacyBanner": {
|
|
"enabled": false,
|
|
"title": "Privacy notice",
|
|
"body": "This instance processes pad content on our servers. See the linked policy for retention and how to request erasure.",
|
|
"learnMoreUrl": null,
|
|
"dismissal": "dismissible"
|
|
},
|
|
|
|
/*
|
|
* From Etherpad 1.8.5 onwards, when Etherpad is in production mode commits from individual users are rate limited
|
|
*
|
|
* The default is to allow at most 10 changes per IP in a 1 second window.
|
|
* After that the change is rejected.
|
|
*
|
|
* See https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#websocket-single-connection-prevent-flooding for more options
|
|
*/
|
|
"commitRateLimiting": {
|
|
// duration of the rate limit window (seconds)
|
|
"duration": 1,
|
|
|
|
// maximum number of changes per IP to allow during the rate limit window
|
|
"points": 10
|
|
},
|
|
|
|
/*
|
|
* Toolbar buttons configuration.
|
|
*
|
|
* Uncomment to customize.
|
|
*/
|
|
|
|
/*
|
|
"toolbar": {
|
|
"left": [
|
|
["bold", "italic", "underline", "strikethrough"],
|
|
["orderedlist", "unorderedlist", "indent", "outdent"],
|
|
["undo", "redo"],
|
|
["clearauthorship"]
|
|
],
|
|
"right": [
|
|
["importexport", "timeslider", "savedrevision"],
|
|
["settings", "embed", "home"],
|
|
["showusers"]
|
|
],
|
|
"timeslider": [
|
|
["timeslider_export", "timeslider_returnToPad"]
|
|
]
|
|
},
|
|
*/
|
|
|
|
/*
|
|
* Expose Etherpad version in the web interface and in the Server http header.
|
|
*
|
|
* Do not enable on production machines.
|
|
*/
|
|
"exposeVersion": false,
|
|
|
|
/*
|
|
* The log level we are using.
|
|
*
|
|
* Valid values: DEBUG, INFO, WARN, ERROR
|
|
*/
|
|
"loglevel": "INFO",
|
|
|
|
/*
|
|
* The log layout type to use.
|
|
*
|
|
* Valid values: basic, colored
|
|
*/
|
|
"logLayoutType": "colored",
|
|
|
|
/*
|
|
* Override any strings found in locale directories.
|
|
*
|
|
* Format: { "<lang>": { "<key>": "<text>", ... }, ... }
|
|
* Example, per-language Open Graph description for link previews:
|
|
* "customLocaleStrings": {
|
|
* "en": { "pad.social.description": "Our team's collaborative pads." },
|
|
* "de": { "pad.social.description": "Kollaborative Notizblöcke." }
|
|
* }
|
|
* For a single description regardless of language, prefer
|
|
* `socialMeta.description` above — link-preview crawlers usually don't
|
|
* send Accept-Language and otherwise hit the English fallback.
|
|
*/
|
|
"customLocaleStrings": {},
|
|
|
|
/* Disable Admin UI tests */
|
|
"enableAdminUITests": false,
|
|
|
|
/*
|
|
* Enable/Disable case-insensitive pad names.
|
|
*/
|
|
"lowerCasePadIds": false,
|
|
|
|
"sso": {
|
|
"issuer": "${SSO_ISSUER:http://localhost:9001}",
|
|
"clients": [
|
|
{
|
|
"client_id": "${ADMIN_CLIENT:admin_client}",
|
|
"client_secret": "${ADMIN_SECRET:admin}",
|
|
"grant_types": ["authorization_code"],
|
|
"response_types": ["code"],
|
|
"redirect_uris": ["${ADMIN_REDIRECT:http://localhost:9001/admin/}"]
|
|
},
|
|
{
|
|
"client_id": "${USER_CLIENT:user_client}",
|
|
"client_secret": "${USER_SECRET:user}",
|
|
"grant_types": ["authorization_code"],
|
|
"response_types": ["code"],
|
|
"redirect_uris": ["${USER_REDIRECT:http://localhost:9001/}"]
|
|
}
|
|
]
|
|
}
|
|
|
|
/* Set the time to live for the tokens
|
|
This is the time of seconds a user is logged into Etherpad
|
|
"ttl": {
|
|
"AccessToken": 3600,
|
|
"AuthorizationCode": 600,
|
|
"ClientCredentials": 3600,
|
|
"IdToken": 3600,
|
|
"RefreshToken": 86400
|
|
}
|
|
*/
|
|
}
|