Skip to content

Env-var overrides for offline/air-gapped installs (update check, plugin catalog, updater)#7917

Open
JohnMcLear wants to merge 3 commits into
developfrom
offline-env-overrides-7911
Open

Env-var overrides for offline/air-gapped installs (update check, plugin catalog, updater)#7917
JohnMcLear wants to merge 3 commits into
developfrom
offline-env-overrides-7911

Conversation

@JohnMcLear

@JohnMcLear JohnMcLear commented Jun 8, 2026

Copy link
Copy Markdown
Member

PR Summary by Qodo

Add env-var overrides to disable update checks & plugin catalog for offline installs
🐞 Bug fix ⚙️ Configuration changes 📝 Documentation 🧪 Tests 🕐 20-40 Minutes

Grey Divider

Walkthroughs

User Description

What

Addresses item 2 of #7911: air-gapped / firewalled deployments could not disable Etherpad's outbound calls purely via environment variables — settings.json.docker hardcoded updates.tier and shipped no privacy block at all, so operators had to edit settings.json inside the image.

This wires the relevant keys through the existing ${ENV:default} substitution (Settings.ts lookupEnvironmentVariables) in both shipped settings files. No code changes — config + docs + tests only.

New environment variables

Variable Setting Default
PRIVACY_UPDATE_CHECK privacy.updateCheck true
PRIVACY_PLUGIN_CATALOG privacy.pluginCatalog true
UPDATES_TIER updates.tier (off = no calls) notify
UPDATES_SOURCE updates.source github
UPDATES_CHANNEL updates.channel stable
UPDATES_CHECK_INTERVAL_HOURS updates.checkIntervalHours 6
UPDATES_GITHUB_REPO updates.githubRepo ether/etherpad
UPDATES_REQUIRE_ADMIN_FOR_STATUS updates.requireAdminForStatus false
UPDATE_SERVER updateServer https://etherpad.org/ep_infos

Fully offline: UPDATES_TIER=off, PRIVACY_UPDATE_CHECK=false, PRIVACY_PLUGIN_CATALOG=false.

Docs

  • New "Updates & privacy (offline / air-gapped)" section in doc/docker.md.
  • Cross-link + Docker note added to doc/admin/updates.md "Disabling everything".

Tests

Added backend regression tests (src/tests/backend/specs/settings.ts) that parse the shipped settings.json.docker and settings.json.template and assert the overrides apply with correct boolean/numeric coercion — so a future edit that drops the ${ENV} placeholders fails loudly. 30 passing.

Scope note

This is item 2 only. Item 1 (the packageManager/corepack startup-failure claim) does not reproduce on the official Docker image on develop — it already installs pnpm via npm (corepack was dropped for Node 25) and runs node directly, with no package manager invoked at startup. Awaiting the reporter's confirmation of whether they hit it on the official image vs. a source build before touching anything there. See discussion on #7911.

🤖 Generated with Claude Code

AI Description
• Expose update/privacy settings as env-var overrides for Docker and template installs.
• Add offline/air-gapped documentation and cross-links for disabling all outbound calls.
• Add regression tests to ensure shipped settings keep ${ENV:default} placeholders and type
  coercion.
Diagram
graph TD
  A["Environment variables"] --> B["settings.json.docker"] --> D["Settings loader (parseSettings)"] --> E["Update check"]
  A --> C["settings.json.template"] --> D --> F["Plugin catalog"]
  D --> G["Self-updater"]
  H["Docs: docker/admin"] --> A
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Single OFFLINE/AIRGAPPED mode flag
  • ➕ Simpler operator experience (one variable to set).
  • ➕ Avoids partial configurations (e.g., disabling tier but not plugin catalog).
  • ➖ Adds new semantics/branching in settings parsing or runtime code.
  • ➖ Less flexible for users who want some calls but not others.
2. Docker entrypoint rewrites settings.json at startup
  • ➕ Keeps settings.json.docker free of placeholders and env parsing concerns.
  • ➕ Can validate values more strictly before writing config.
  • ➖ More moving parts and maintenance burden in container startup scripts.
  • ➖ Harder to keep parity with non-Docker installs (template).

Recommendation: The chosen approach (adding ${ENV:default} placeholders to shipped settings templates and enforcing them via regression tests) is the best fit: it reuses the existing Settings.ts substitution/coercion logic, avoids runtime behavior changes, and works consistently for both Docker and source installs. A single OFFLINE flag could be considered later as a convenience wrapper, but the current granular env vars are more flexible and lower-risk.

Grey Divider

File Changes

Tests (1)
settings.ts Add regression tests for offline env overrides in shipped settings files +81/-0

Add regression tests for offline env overrides in shipped settings files

• Adds tests that parse the repository's shipped settings.json.docker and settings.json.template and assert env-var overrides apply as expected. Verifies boolean/numeric coercion and protects against future removal of ${ENV} placeholders.

src/tests/backend/specs/settings.ts


Documentation (2)
updates.md Document Docker env vars for disabling update checks +2/-0

Document Docker env vars for disabling update checks

• Adds guidance for Docker/air-gapped deployments to disable update-related outbound calls via environment variables (not editing settings inside the image). Links readers to the new Docker docs table for the full variable set.

doc/admin/updates.md


docker.md Add offline/air-gapped update & privacy env-var reference table +19/-0

Add offline/air-gapped update & privacy env-var reference table

• Introduces a dedicated section describing Etherpad's outbound update/plugin-catalog calls and how to disable them in air-gapped deployments. Documents all new environment variables and provides a 'fully offline' recommended combination.

doc/docker.md


Other (2)
settings.json.docker Make updates/privacy settings overridable via env vars (Docker image) +21/-7

Make updates/privacy settings overridable via env vars (Docker image)

• Replaces hardcoded updates.* values with ${ENV:default} placeholders and makes updateServer configurable via UPDATE_SERVER. Adds a privacy block with env-var toggles for update checks and plugin catalog access, enabling fully offline behavior without editing the image.

settings.json.docker


settings.json.template Expose offline update/privacy env vars in source-install template +7/-5

Expose offline update/privacy env vars in source-install template

• Adds ${UPDATES_TIER:notify} and ${UPDATE_SERVER:...} placeholders and wires privacy.updateCheck/pluginCatalog through env vars. Updates comments to point to doc/privacy.md and to describe air-gapped configuration.

settings.json.template


Grey Divider

Qodo Logo

…ater (offline installs)

Air-gapped and firewalled deployments could not disable Etherpad's outbound
calls (the hourly version check, the admin plugin catalogue, and the
self-updater) without editing settings.json inside the container image — the
shipped settings.json.docker hardcoded updates.tier and omitted the privacy
block entirely, so there was no env-var to flip.

Wire the relevant keys through the existing ${ENV:default} substitution in both
settings.json.docker and settings.json.template:

  - PRIVACY_UPDATE_CHECK            (privacy.updateCheck, default true)
  - PRIVACY_PLUGIN_CATALOG          (privacy.pluginCatalog, default true)
  - UPDATES_TIER                    (updates.tier, default notify; "off" = no calls)
  - UPDATES_SOURCE / UPDATES_CHANNEL / UPDATES_CHECK_INTERVAL_HOURS /
    UPDATES_GITHUB_REPO / UPDATES_REQUIRE_ADMIN_FOR_STATUS (docker)
  - UPDATE_SERVER                   (updateServer endpoint)

Document the full set in doc/docker.md (new "Updates & privacy" section) and
cross-link from doc/admin/updates.md. Add backend regression tests that parse
the shipped settings.json.docker and settings.json.template and assert the
overrides apply with correct boolean/numeric coercion, so a future edit that
drops the ${ENV} placeholders fails loudly.

Addresses #7911 (item 2). No code changes — config + docs + tests only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 8, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0)

Grey Divider


Remediation recommended

1. Outbound-call doc mislinked ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
New docs and settings-file comments direct operators to doc/privacy.md for outbound network call
details, but the outbound-call inventory and disable instructions are in the repo-root PRIVACY.md.
This can mislead offline operators and conflicts with runtime messages that explicitly reference
PRIVACY.md.
Code

settings.json.template[R449-455]

+   * Outbound network calls. See doc/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)
+   * Air-gapped / firewalled deployments should set both PRIVACY_UPDATE_CHECK and
+   * PRIVACY_PLUGIN_CATALOG to false (or UPDATES_TIER=off, which covers the version check).
 */
Evidence
PRIVACY.md is explicitly the outbound-call document, while doc/privacy.md is about what Etherpad
stores/logs. Runtime guards and messages for update checks and plugin catalog also reference
PRIVACY.md, so pointing to doc/privacy.md is inconsistent and misleading.

doc/docker.md[121-135]
settings.json.docker[326-337]
settings.json.template[446-455]
PRIVACY.md[1-37]
doc/privacy.md[1-5]
src/static/js/pluginfw/pluginCatalogGuard.ts[1-10]
src/node/utils/UpdateCheck.ts[41-66]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Multiple newly-added references say outbound network calls are documented in `doc/privacy.md`, but that file is about user data/logging; the outbound-call documentation is in the repo-root `PRIVACY.md`. This can send operators to the wrong document when trying to disable outbound requests.
### Issue Context
- `PRIVACY.md` explicitly documents the version check + plugin catalog outbound calls and how to disable them.
- Runtime errors/info messages also say “see PRIVACY.md”.
- `doc/privacy.md` is a different document (data-processing statement), so the current links/comments are misleading.
### Fix Focus Areas
- doc/docker.md[121-135]
- settings.json.docker[328-337]
- settings.json.template[448-455]
Suggested change: replace `doc/privacy.md` references with `PRIVACY.md` (and in `doc/docker.md` use the correct relative link to the repo root, e.g. `../PRIVACY.md`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Overstates tier=off effect ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
doc/admin/updates.md claims updates.tier="off" means no HTTP request will leave the instance,
but UpdateCheck.ts will still fetch ${updateServer}/info.json unless privacy.updateCheck is
disabled. This can cause unexpected outbound attempts/log noise in air-gapped deployments that only
set UPDATES_TIER=off.
Code

doc/admin/updates.md[R105-107]

Set `updates.tier` to `"off"`. No HTTP request will leave the instance and no banner or badge will render.
+On Docker / air-gapped installs you can do this without editing `settings.json` inside the image by setting `UPDATES_TIER=off` (and, to also drop the separate `updateServer` version check, `PRIVACY_UPDATE_CHECK=false`). See the [Updates & privacy](../docker.md#updates--privacy-offline--air-gapped) table in the Docker docs for the full set of environment variables.
Evidence
The updater’s network polling is gated by updates.tier, but the separate version check uses
privacy.updateCheck and still performs a fetch against updateServer. Therefore,
updates.tier="off" does not guarantee zero outbound HTTP requests.

doc/admin/updates.md[103-108]
src/node/updater/index.ts[113-115]
src/node/utils/UpdateCheck.ts[13-44]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The docs currently state that setting `updates.tier` to `off` stops all HTTP requests. In reality, it only disables the self-updater polling (GitHub Releases); the separate `updateServer`-based version check still runs unless `privacy.updateCheck=false`.
### Issue Context
- `src/node/updater/index.ts` skips updater polling when `settings.updates.tier === 'off'`.
- `src/node/utils/UpdateCheck.ts` performs a fetch to `settings.updateServer` gated only by `settings.privacy.updateCheck`.
### Fix Focus Areas
- doc/admin/updates.md[103-108]
Suggested change: adjust wording to something like:
- "Set `updates.tier` to `off` to disable the self-updater (GitHub Releases polling)." 
- "To stop the separate version check, also set `privacy.updateCheck=false` (or `PRIVACY_UPDATE_CHECK=false` in Docker)."

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

- Outbound-call docs/comments referenced doc/privacy.md (the storage/logging
  doc); the canonical outbound-call inventory is repo-root PRIVACY.md, which the
  runtime messages in UpdateCheck.ts / Settings.ts also reference. Re-point
  settings.json.{template,docker} and doc/docker.md there.
- doc/admin/updates.md said updates.tier="off" means "no HTTP request will leave
  the instance", but the legacy UpdateCheck.ts call to ${updateServer}/info.json
  is gated by privacy.updateCheck, not updates.tier. Clarify that air-gapped
  installs must set PRIVACY_UPDATE_CHECK=false too.

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

Copy link
Copy Markdown
Member Author

Thanks Qodo — both findings actioned in 3a95519:

  1. Outbound-call doc mislinked. Correct — repo-root PRIVACY.md is the canonical outbound-call inventory (and what UpdateCheck.ts/Settings.ts reference at runtime), whereas doc/privacy.md is the storage/logging doc. Re-pointed settings.json.template, settings.json.docker, and doc/docker.md to PRIVACY.md.
  2. Overstates tier=off effect. Correct — the legacy UpdateCheck.ts fetch of ${updateServer}/info.json is gated by privacy.updateCheck, not updates.tier. Reworded the "Disabling everything" section to scope tier=off to the self-updater and spell out that air-gapped installs must also set PRIVACY_UPDATE_CHECK=false (and PRIVACY_PLUGIN_CATALOG=false for the catalogue).

… check

PRIVACY.md lives at the repo root, outside the doc/ tree VitePress builds, so a
relative link to it (../PRIVACY.md / ../../PRIVACY.md) is flagged as a dead link
and fails `docs:build`. Use the absolute GitHub URL instead, matching how
doc/configuration.md already links settings.json.template.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JohnMcLear JohnMcLear requested a review from SamTV12345 June 9, 2026 08:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant