Skip to content

Make external remote plugins available directly from CoMPAS nginx container (air-gapped) #510

Description

@david-monichi

User Story

As a DevOps engineer

I want to define a list of plugins via a configuration file using a remote URL and a hash code for validation

So that they are available to the editor by downloading them from the CoMPAS nginx service without the need to connect to external sources

Description

The CoMPAS nginx container should serve external plugins locally instead of the browser fetching them from third-party hosts at runtime.
Plugins are declared in a root-level remote-plugins.json file, where each entry is an object with the following shape:

{
  "name": "Custom Plugin",
  "url": "https://example.com/compas/custom-plugin/1.0.0/custom-plugin.js",
  "dest": "path/to/custom-plugin.js", // defaults to the last path segment of the URL if omitted
  "sha256": "d84da37a20f55716999d48faa0990a28ad3e0683b5d351467f6b78a27a21c5bc" // optional
}
Field Required Purpose
name yes Human-readable identifier shown in logs and used to look the entry up.
url yes Remote URL the plugin JavaScript is downloaded from at container build time.
dest yes Path (relative to /usr/share/nginx/html/external-plugins/) where the file is placed inside the container; also determines the URL nginx serves it under.
sha256 no Hex-encoded SHA-256 digest of the expected file content. When present, the container build fails if the downloaded bytes do not match. Leave empty ("") or omit to skip the integrity check for that plugin.

To make this possible, the following needs to be delivered:

  • A distribution/remote-plugins.json file listing every external plugin.
  • A multi-stage distribution/Dockerfile in which a builder stage reads remote-plugins.json and, for each entry, downloads-and-verifies the plugin as a single atomic step before copying the files into the final nginx image under /usr/share/nginx/html/external-plugins/.
  • A shell downloader script (distribution/scripts/download-plugins.sh) invoked by the builder stage. For every plugin it downloads the bytes and, if a sha256 is declared, immediately verifies the digest against them; any download error or hash mismatch aborts the build before the next plugin is processed.
  • A Node.js helper script for managing plugins — see sub-issue Implement Node.js helper script for managing remote plugins (scripts/manage-plugins.js) #527.
  • User-facing documentation in docs/how-to/remote-plugins.md.

Acceptance Criteria

Product behaviour

  • The CoMPAS nginx service serves the defined plugins to the editor
  • The editor downloads plugins from the CoMPAS nginx service without direct access to external sources

Configuration file

  • The configuration file lives at (distribution/remote-plugins.json) and is copied into the Docker build context
  • Each plugin entry defines a destination path controlling where the file lands inside the container and the URL it is served at
  • The configuration file supports an optional hash field per plugin for integrity validation
  • When no hash is provided for a plugin, the bytes are still downloaded but the integrity check is skipped and the build succeeds
  • When present, sha256 is validated as a 64-character lowercase hex string; an empty string or omitted field is accepted
  • dest must not be absolute and must not contain .. segments (path-traversal protection)
  • An empty plugin list is valid and produces a successful build with no external plugins served
  • A malformed configuration file (invalid JSON, missing plugins key, or non-array plugins) causes the build to fail

Download & verify (distribution/scripts/download-plugins.sh)

  • Each plugin is downloaded and (when a hash is declared) verified as one atomic operation per entry; a failure of either step aborts the whole build immediately
  • If a plugin download fails, the container build must fail immediately, before subsequent plugins are processed
  • For every plugin, downloading and hash verification are performed together as a single atomic step during the container build: bytes are fetched and, if a hash is declared, immediately compared against it before moving on to the next plugin
  • An empty (0-byte) downloaded plugin file causes the container build to fail, regardless of whether a hash is declared
  • All plugin downloads use https:// URLs
  • If hash verification fails for a plugin that declared a hash, the container build must fail immediately, before subsequent plugins are processed
  • distribution/scripts/download-plugins.sh and distribution/scripts/manage-plugins.js verify apply the same validation rules (schema, empty download, hash mismatch) so a passing local verify implies a passing Docker build

Dockerfile

  • The nginx container is built using a multi-stage Docker build, separating the download-and-verify stage from serving
  • The multi-stage build minimises layer invalidation so only plugin-related layers are rebuilt when remote-plugins.json changes

Editor wiring

  • Every plugin served from the container is referenced in public/public/js/plugins.js via a /external-plugins/<dest> path (no external hosts)

Documentation

  • Documentation describes the remote-plugins.json schema, including the fact that sha256 is optional and recommends providing it for all production plugins
  • Documentation describes how to use the helper script (add, update, verify)
  • Documentation includes a worked example of adding a plugin end-to-end (from URL to running container)
  • Documentation describes how to update / rotate an existing plugin's URL or hash
  • Documentation states that the shell downloader script runs only inside the Docker build and that contributors on any OS use the npm scripts

Metadata

Metadata

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions