etherpad-lite/settings.json.docker
John McLear 962bfe8649
feat(updater): tier 4 — autonomous update in maintenance window (#7607) (#7753)
* 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>
2026-05-17 13:50:34 +01:00

842 lines
31 KiB
Docker

/**
* THIS IS THE SETTINGS FILE THAT IS COPIED INSIDE THE DOCKER CONTAINER.
*
* By default, some runtime customizations are supported (see the
* documentation).
*
* If you need more control, edit this file and rebuild the container.
*/
/*
* 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": "${TITLE:Etherpad}",
/*
* Whether to show recent pads on the homepage or not.
*/
"showRecentPads": "${SHOW_RECENT_PADS: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": "${FAVICON:null}",
/*
* Canonical public origin of this Etherpad instance, e.g.
* "https://pad.example.com" (no trailing slash, must include scheme).
* Used to build absolute URLs in OG/Twitter link-preview meta tags.
* When null, falls back to the incoming request's protocol+Host.
*/
"publicURL": "${PUBLIC_URL:null}",
/*
* Open Graph / Twitter Card metadata for link previews.
*
* SOCIAL_META_DESCRIPTION: when set, this exact text is used as
* og:description regardless of negotiated language. Most preview crawlers
* (WhatsApp, Signal, Slack, ...) don't send Accept-Language, so without an
* override they always hit the English fallback in the i18n catalog.
* Leave unset (null) to use the catalog (key `pad.social.description`).
*/
"socialMeta": {
"description": "${SOCIAL_META_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": "${SKIN_NAME: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": "${SKIN_VARIANTS: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": "${IP:0.0.0.0}",
"port": "${PORT:9001}",
/*
* Option to hide/show the settings.json in admin page.
*
* Default option is set to true
*/
"showSettingsInAdminPage": "${SHOW_SETTINGS_IN_ADMIN_PAGE: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.
* Docker installs are read-only — tiers above "notify" are not applied even if requested.
*/
"updates": {
"tier": "notify",
"source": "github",
"channel": "stable",
"installMethod": "docker",
"checkIntervalHours": 6,
"githubRepo": "ether/etherpad",
"requireAdminForStatus": false,
"preApplyGraceMinutes": 0,
"drainSeconds": 60,
"rollbackHealthCheckSeconds": 60,
"diskSpaceMinMB": 500,
"requireSignature": false,
"trustedKeysPath": null,
"maintenanceWindow": null
},
/*
* Contact address for admin notifications (updates, security advisories, future features).
* Set to null to disable outbound mail from the updater.
*/
"adminEmail": null,
/*
* SMTP transport. host=null keeps log-only behaviour; set host+from to send
* real mail via nodemailer (lazy-loaded). Pulls from env vars by default —
* leave the template as-is and provide MAIL_HOST / MAIL_FROM at runtime.
*/
"mail": {
"host": "${MAIL_HOST:null}",
"port": "${MAIL_PORT:587}",
"secure": "${MAIL_SECURE:false}",
"from": "${MAIL_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.
*/
"gdprAuthorErasure": {
"enabled": "${GDPR_AUTHOR_ERASURE_ENABLED:false}"
},
/*
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}",
/*
* Optional privacy banner. See settings.json.template for full field docs.
*/
"privacyBanner": {
"enabled": "${PRIVACY_BANNER_ENABLED:false}",
"title": "${PRIVACY_BANNER_TITLE:Privacy notice}",
"body": "${PRIVACY_BANNER_BODY:This instance processes pad content on our servers. See the linked policy for retention and how to request erasure.}",
"learnMoreUrl": "${PRIVACY_BANNER_LEARN_MORE_URL:null}",
"dismissal": "${PRIVACY_BANNER_DISMISSAL:dismissible}"
},
/*
* 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"]
},
*/
/*
* 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",
/*
* 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": "${DB_TYPE:dirty}",
"dbSettings": {
"host": "${DB_HOST:undefined}",
"port": "${DB_PORT:undefined}",
"database": "${DB_NAME:undefined}",
"user": "${DB_USER:undefined}",
"password": "${DB_PASS:undefined}",
"charset": "${DB_CHARSET:undefined}",
"filename": "${DB_FILENAME:var/dirty.db}",
"collection": "${DB_COLLECTION:undefined}",
"url": "${DB_URL:undefined}"
},
/*
* The default text of a pad
*/
"defaultPadText" : "${DEFAULT_PAD_TEXT: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": "${PAD_OPTIONS_NO_COLORS:false}",
"showControls": "${PAD_OPTIONS_SHOW_CONTROLS:true}",
"showChat": "${PAD_OPTIONS_SHOW_CHAT:true}",
"showLineNumbers": "${PAD_OPTIONS_SHOW_LINE_NUMBERS:true}",
"useMonospaceFont": "${PAD_OPTIONS_USE_MONOSPACE_FONT:false}",
"userName": "${PAD_OPTIONS_USER_NAME:null}",
"userColor": "${PAD_OPTIONS_USER_COLOR:null}",
"rtl": "${PAD_OPTIONS_RTL:false}",
"alwaysShowChat": "${PAD_OPTIONS_ALWAYS_SHOW_CHAT:false}",
"chatAndUsers": "${PAD_OPTIONS_CHAT_AND_USERS:false}",
"lang": "${PAD_OPTIONS_LANG:null}",
"fadeInactiveAuthorColors": "${PAD_OPTIONS_FADE_INACTIVE_AUTHOR_COLORS:true}",
"enforceReadableAuthorColors": "${PAD_OPTIONS_ENFORCE_READABLE_AUTHOR_COLORS:true}"
},
/*
* Pad Shortcut Keys
*/
"padShortcutEnabled" : {
"altF9": "${PAD_SHORTCUTS_ENABLED_ALT_F9:true}", /* focus on the File Menu and/or editbar */
"altC": "${PAD_SHORTCUTS_ENABLED_ALT_C:true}", /* focus on the Chat window */
"cmdShift2": "${PAD_SHORTCUTS_ENABLED_CMD_SHIFT_2:true}", /* shows a gritter popup showing a line author */
"delete": "${PAD_SHORTCUTS_ENABLED_DELETE:true}",
"return": "${PAD_SHORTCUTS_ENABLED_RETURN:true}",
"esc": "${PAD_SHORTCUTS_ENABLED_ESC:true}", /* in mozilla versions 14-19 avoid reconnecting pad */
"cmdS": "${PAD_SHORTCUTS_ENABLED_CMD_S:true}", /* save a revision */
"tab": "${PAD_SHORTCUTS_ENABLED_TAB:true}", /* indent */
"cmdZ": "${PAD_SHORTCUTS_ENABLED_CMD_Z:true}", /* undo/redo */
"cmdY": "${PAD_SHORTCUTS_ENABLED_CMD_Y:true}", /* redo */
"cmdI": "${PAD_SHORTCUTS_ENABLED_CMD_I:true}", /* italic */
"cmdB": "${PAD_SHORTCUTS_ENABLED_CMD_B:true}", /* bold */
"cmdU": "${PAD_SHORTCUTS_ENABLED_CMD_U:true}", /* underline */
"cmd5": "${PAD_SHORTCUTS_ENABLED_CMD_5:true}", /* strike through */
"cmdShiftL": "${PAD_SHORTCUTS_ENABLED_CMD_SHIFT_L:true}", /* unordered list */
"cmdShiftN": "${PAD_SHORTCUTS_ENABLED_CMD_SHIFT_N:true}", /* ordered list */
"cmdShift1": "${PAD_SHORTCUTS_ENABLED_CMD_SHIFT_1:true}", /* ordered list */
"cmdShiftC": "${PAD_SHORTCUTS_ENABLED_CMD_SHIFT_C:true}", /* clear authorship */
"cmdH": "${PAD_SHORTCUTS_ENABLED_CMD_H:true}", /* backspace */
"ctrlHome": "${PAD_SHORTCUTS_ENABLED_CTRL_HOME:true}", /* scroll to top of pad */
"pageUp": "${PAD_SHORTCUTS_ENABLED_PAGE_UP:true}",
"pageDown": "${PAD_SHORTCUTS_ENABLED_PAGE_DOWN:true}"
},
/*
* Should we suppress errors from being visible in the default Pad Text?
*/
"suppressErrorsInPadText": "${SUPPRESS_ERRORS_IN_PAD_TEXT: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": "${REQUIRE_SESSION: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": "${EDIT_ONLY: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": "${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": "${MAX_AGE: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": "${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": "${DOCX_EXPORT:true}",
/*
* txt, doc, docx, rtf, odt, html & htm
*/
"allowUnknownFileEnds": "${ALLOW_UNKNOWN_FILE_ENDS:true}",
/*
* This setting is used if you require authentication of all users.
*
* Note: "/admin" always requires authentication.
*/
"requireAuthentication": "${REQUIRE_AUTHENTICATION:false}",
/*
* Require authorization by a module, or a user with is_admin set, see below.
*/
"requireAuthorization": "${REQUIRE_AUTHORIZATION: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": "${TRUST_PROXY:false}",
/*
* Settings controlling the session cookie issued by Etherpad.
*/
"cookie": {
/*
* 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).
*/
// 86400000 = 1d * 24h/d * 60m/h * 60s/m * 1000ms/s
"keyRotationInterval": "${COOKIE_KEY_ROTATION_INTERVAL:86400000}",
/*
* 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": "${COOKIE_SAME_SITE: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.
*/
// 864000000 = 10d * 24h/d * 60m/h * 60s/m * 1000ms/s
"sessionLifetime": "${COOKIE_SESSION_LIFETIME:864000000}",
/*
* 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.
*/
// 86400000 = 1d * 24h/d * 60m/h * 60s/m * 1000ms/s
"sessionRefreshInterval": "${COOKIE_SESSION_REFRESH_INTERVAL:86400000}"
},
/*
* Controls what Etherpad writes to its logs about client IP addresses.
* Allowed values: "anonymous" (default), "truncated", "full".
* See settings.json.template for details.
*/
"ipLogging": "${IP_LOGGING:anonymous}",
/*
* Deprecated — use `ipLogging` above. Still honoured for one release
* cycle: true"anonymous", false"full".
*/
"disableIPlogging": "${DISABLE_IP_LOGGING: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": "${ALLOW_PAD_DELETION_BY_ALL_USERS: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": "${AUTOMATIC_RECONNECTION_TIMEOUT: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": "${FOCUS_LINE_PERCENTAGE_ABOVE:0}",
"editionBelowViewport": "${FOCUS_LINE_PERCENTAGE_BELOW:0}"
},
/*
* Time (in milliseconds) used to animate the scroll transition.
* Set to 0 to disable animation
*/
"duration": "${FOCUS_LINE_DURATION:0}",
/*
* Flag to control if it should scroll when user places the caret in the
* last line of the viewport
*/
"scrollWhenCaretIsInTheLastLineOfViewport": "${FOCUS_LINE_CARET_SCROLL: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": "${FOCUS_LINE_PERCENTAGE_ARROW_UP: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": "${ADMIN_PASSWORD:null}",
"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": "${USER_PASSWORD:null}",
"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": "${SOCKETIO_MAX_HTTP_BUFFER_SIZE:1000000}"
},
/*
* Allow Load Testing tools to hit the Etherpad Instance.
*
* WARNING: this will disable security on the instance.
*/
"loadTest": "${LOAD_TEST:false}",
/**
* Disable dump of objects preventing a clean exit
*/
"dumpOnUncleanExit": "${DUMP_ON_UNCLEAN_EXIT: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": "${IMPORT_EXPORT_RATE_LIMIT_WINDOW:90000}",
// maximum number of requests per IP to allow during the rate limit window
"max": "${IMPORT_EXPORT_MAX_REQ_PER_IP: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": "${IMPORT_MAX_FILE_SIZE:52428800}", // 50 * 1024 * 1024
/*
* 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": "${COMMIT_RATE_LIMIT_DURATION:1}",
// maximum number of changes per IP to allow during the rate limit window
"points": "${COMMIT_RATE_LIMIT_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": "${EXPOSE_VERSION:false}",
/*
* The log level we are using.
*
* Valid values: DEBUG, INFO, WARN, ERROR
*/
"loglevel": "${LOGLEVEL:INFO}",
/* Override any strings found in locale directories */
"customLocaleStrings": {},
/* Disable Admin UI tests */
"enableAdminUITests": false,
/*
* Enable/Disable case-insensitive pad names.
*/
"lowerCasePadIds": "${LOWER_CASE_PAD_IDS: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
}
*/
}