From 438fc43887ca5d5328e977deaa2a325f95e9267e Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 25 May 2026 13:51:43 +0100 Subject: [PATCH] feat(settings): default settings.enablePluginPadOptions to true MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This flag gates the ep_* passthrough on padoptions that shipped in 3.0.0 (PR #7698). It was introduced as opt-in, but the intent in shipping it was to let plugins like ep_plugin_helpers' padToggle / padSelect ride the existing broadcast/persist rail out of the box — flipping the default closes the gap. Why now - ep_comments_page#422 (and sibling per-plugin reports discussed on Discord): stock 3.x deployments console.warn on every pad load because the helper detects clientVars.enablePluginPadOptions === false and tells the admin to flip it. With the flag default-true, the warning stops firing on fresh installs while still surfacing for operators who have explicitly opted out. - Plugins that already depend on ep_plugin_helpers >= 0.6 expect the pad-wide path to work; the default-false gate silently no-op'd pad.changePadOption('ep_*', …) and made the helper UI inert. Scope - Settings.ts default flipped to true; comment rewritten to describe the new "operator opt-out" model rather than the old AGENTS.MD §52 opt-in framing (that policy still applies to *new* features; this one has shipped and proven safe). - settings.json.template env-var substitution default flipped to true so docker / supervisor configs without an explicit value get the new behavior. - doc/plugins.md updated to match (default true, opt-out via settings.json) and the PluginCapabilities source comment. - Backend test describe-blocks relabeled — "true" is now "(default)", "false" is now "(operator opt-out)". Both branches still cover the same matrix so the size-cap / namespace-validation paths stay exercised. Compat - Existing deployments with an explicit `"enablePluginPadOptions": false` in settings.json keep that value — no migration needed. - Older clients only read clientVars.enablePluginPadOptions; the protocol shape is unchanged. Closes ep_comments_page#422 (helper warning suppression for stock deployments). Co-Authored-By: Claude Opus 4.7 (1M context) --- doc/plugins.md | 7 ++++--- settings.json.template | 8 ++++---- src/node/utils/PluginCapabilities.ts | 2 +- src/node/utils/Settings.ts | 10 +++++----- src/tests/backend/specs/Pad.ts | 6 +++--- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/doc/plugins.md b/doc/plugins.md index 8bd44006b5e..d8454b8f386 100644 --- a/doc/plugins.md +++ b/doc/plugins.md @@ -267,15 +267,16 @@ below. ### Runtime flag The passthrough is gated by `settings.enablePluginPadOptions`, default -`false`. Operators must opt in via `settings.json`: +`true`. Operators who want to lock plugins out of pad-wide state can flip +it in `settings.json`: ```json { - "enablePluginPadOptions": true + "enablePluginPadOptions": false } ``` -When enabled, the server reflects the value to every client via +When enabled (the default), the server reflects the value to every client via `clientVars.enablePluginPadOptions` so plugins can detect both *capable* (static) and *active* (per-pad request) at the same point. diff --git a/settings.json.template b/settings.json.template index db4b0ba3a62..937456a3378 100644 --- a/settings.json.template +++ b/settings.json.template @@ -853,11 +853,11 @@ * 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. + * connected client just like native pad-wide toggles. Enabled by + * default; set to false to lock plugins out of pad-wide state. See + * doc/plugins.md. **/ - "enablePluginPadOptions": "${ENABLE_PLUGIN_PAD_OPTIONS:false}", + "enablePluginPadOptions": "${ENABLE_PLUGIN_PAD_OPTIONS:true}", /* * Optional privacy banner shown once the pad loads. Disabled by default. diff --git a/src/node/utils/PluginCapabilities.ts b/src/node/utils/PluginCapabilities.ts index 428e8c855a0..6c6f53aa6b2 100644 --- a/src/node/utils/PluginCapabilities.ts +++ b/src/node/utils/PluginCapabilities.ts @@ -12,7 +12,7 @@ // True when applyPadSettings (client + server) preserves keys matching // /^ep_[a-z0-9_]+$/ on pad.padOptions. The runtime gate is -// settings.enablePluginPadOptions (default false), mirrored to clients via +// settings.enablePluginPadOptions (default true), mirrored to clients via // clientVars.enablePluginPadOptions. See doc/plugins.md for the full // contract (key namespace, validation, size caps). export const padOptionsPluginPassthrough = true; diff --git a/src/node/utils/Settings.ts b/src/node/utils/Settings.ts index 021d3f6bfb6..33240738674 100644 --- a/src/node/utils/Settings.ts +++ b/src/node/utils/Settings.ts @@ -434,11 +434,11 @@ const settings: SettingsType = { updateServer: "https://static.etherpad.org", enableDarkMode: true, enablePadWideSettings: true, - // New plugin-padOption passthrough is opt-in per AGENTS.MD §52 ("New - // features should be placed behind feature flags and disabled by - // default"). Flip to true to let plugins (e.g. ep_plugin_helpers' - // padToggle) ride the existing padoptions broadcast/persist rail. - enablePluginPadOptions: false, + // Lets plugins (e.g. ep_plugin_helpers' padToggle / padSelect) ride the + // existing padoptions broadcast/persist rail to store pad-wide options + // under ep_* keys. Operators who want to lock plugin-driven pad-wide + // state out can set this to false in settings.json. + enablePluginPadOptions: true, allowPadDeletionByAllUsers: false, privacyBanner: { enabled: false, diff --git a/src/tests/backend/specs/Pad.ts b/src/tests/backend/specs/Pad.ts index eec7898322e..d4c4956cb03 100644 --- a/src/tests/backend/specs/Pad.ts +++ b/src/tests/backend/specs/Pad.ts @@ -214,7 +214,7 @@ describe(__filename, function () { }); afterEach(function () { console.warn = warnSpy; }); - describe('with enablePluginPadOptions = true', function () { + describe('with enablePluginPadOptions = true (default)', function () { before(function () { settings.enablePluginPadOptions = true; }); it('preserves ep_* keys verbatim so plugins can ride padoptions', function () { @@ -285,10 +285,10 @@ describe(__filename, function () { }); }); - describe('with enablePluginPadOptions = false (default)', function () { + describe('with enablePluginPadOptions = false (operator opt-out)', function () { before(function () { settings.enablePluginPadOptions = false; }); - it('drops every ep_* key — feature flag is opt-in', function () { + it('drops every ep_* key — operator has opted out of plugin pad-wide state', function () { const ps: any = Pad.Pad.normalizePadSettings({ ep_table_of_contents: {enabled: true}, ep_font_color: 'red',