Plugins: one tracked manifest for builtin, official and third-party plugins, and nothing installed outside a manifest #1263

Open
opened 2026-11-13 10:56:29 +00:00 by tiagoagueda · 1 comment
Owner

The idea

One manifest, tracked in core, describes every plugin Postulo knows about: the builtin ones, the official ones, and, by the administrator's choice, third-party ones. Installing and upgrading are one click from that list. A plugin is never added except through a manifest: there is no install from a bare URL, a file or a pip name.

What exists today (read from main, 2026-11-13)

  • catalogue/official/ (plugins.toml, a signed index.json) lists six plugins: helloworld, templates, apprise, dav, imap, paperless. Each release has a wheel URL, SHA-256, compatible Postulo versions, provides and requires.
  • plugins/catalogue.py verifies, in order and fatally: the index signature (Ed25519), the plugin and version, the Postulo compatibility, the wheel's SHA-256. An index is fetched only when an administrator opens the page and asks.
  • Third-party catalogues are already only possible as name|url|public-key in POSTULO_PLUGIN_CATALOGUES; without the key there is no catalogue.
  • plugins/installing.py snapshots and rolls back, so a failed upgrade costs nothing.
  • Not audited: whether any install path exists outside a catalogue. This issue includes checking it and closing it if it does.

Wanted

  1. Builtin and the rest of the official plugins in the manifest. The builtin plugins (shipped inside core: wikidata, register_lookup, maps, publication_lookups, identifiers and the others) get entries marked builtin with the version they ship with and no wheel; mcp, chromium, firefox and thunderbird get entries or a stated reason they have none (the extensions are not Python wheels). The page shows one list, three origins (builtin, official, third-party), with the origin on each row.
  2. Third-party catalogues added from the interface, not only the environment setting: a form for a name, a URL and the public key, with a plain warning that being listed means the publisher of that catalogue looked at the plugin, not that its behaviour is guaranteed (as the page says now). Removing a catalogue is as easy. The environment setting keeps working and wins (a pinned catalogue cannot be removed from the page, as other pinned settings cannot).
  3. Nothing outside a manifest. Audit plugins/views.py and installing.py for any route that installs from a file, a URL or a pip name; if one exists, remove it or put it behind a documented, off-by-default setting for development, with the reason in the decision.
  4. Expiry and signing. The official index expires on 2026-12-31 and is signed with a key held only in Vaultwarden. The release recipe (the 0.5.0 notes) gets a step that re-signs and republishes the index, a check in CI that the index is not within 30 days of expiry, and a page message that says an expired index cannot be used and why, instead of an empty list.
  5. Network rule kept: opening the page fetches the index; nothing is fetched in the background.

Left to #1187

Detecting that an update exists and the one-click upgrade (with the setting the maintainer decided, enable/disable, off by default) stay in #1187; this issue supplies the manifest it reads. Linked both ways.

Tests

The list shows the three origins; a builtin row has no install or remove button but shows its version; a third-party catalogue is added with a key, an unsigned or wrong-key index is refused with a message; the pinned catalogue cannot be removed; no route installs from outside a manifest; an index close to expiry fails CI; an expired index gives the explicit message.

Strings go through Weblate only. Related: #1187, #1252, #1253 to #1262, #729 (the official catalogue), #1170.

## The idea One manifest, tracked in core, describes every plugin Postulo knows about: the **builtin** ones, the **official** ones, and, by the administrator's choice, **third-party** ones. Installing and upgrading are one click from that list. **A plugin is never added except through a manifest**: there is no install from a bare URL, a file or a pip name. ## What exists today (read from `main`, 2026-11-13) - `catalogue/official/` (`plugins.toml`, a signed `index.json`) lists six plugins: helloworld, templates, apprise, dav, imap, paperless. Each release has a wheel URL, SHA-256, compatible Postulo versions, provides and requires. - `plugins/catalogue.py` verifies, in order and fatally: the index signature (Ed25519), the plugin and version, the Postulo compatibility, the wheel's SHA-256. An index is fetched only when an administrator opens the page and asks. - Third-party catalogues are already only possible as `name|url|public-key` in `POSTULO_PLUGIN_CATALOGUES`; without the key there is no catalogue. - `plugins/installing.py` snapshots and rolls back, so a failed upgrade costs nothing. - Not audited: whether any install path exists outside a catalogue. This issue includes checking it and closing it if it does. ## Wanted 1. **Builtin and the rest of the official plugins in the manifest.** The builtin plugins (shipped inside core: wikidata, register_lookup, maps, publication_lookups, identifiers and the others) get entries marked `builtin` with the version they ship with and no wheel; mcp, chromium, firefox and thunderbird get entries or a stated reason they have none (the extensions are not Python wheels). The page shows one list, three origins (builtin, official, third-party), with the origin on each row. 2. **Third-party catalogues added from the interface**, not only the environment setting: a form for a name, a URL and the **public key**, with a plain warning that being listed means the publisher of that catalogue looked at the plugin, not that its behaviour is guaranteed (as the page says now). Removing a catalogue is as easy. The environment setting keeps working and wins (a pinned catalogue cannot be removed from the page, as other pinned settings cannot). 3. **Nothing outside a manifest.** Audit `plugins/views.py` and `installing.py` for any route that installs from a file, a URL or a pip name; if one exists, remove it or put it behind a documented, off-by-default setting for development, with the reason in the decision. 4. **Expiry and signing.** The official index expires on `2026-12-31` and is signed with a key held only in Vaultwarden. The release recipe (the 0.5.0 notes) gets a step that re-signs and republishes the index, a check in CI that the index is not within 30 days of expiry, and a page message that says an expired index cannot be used and why, instead of an empty list. 5. **Network rule kept:** opening the page fetches the index; nothing is fetched in the background. ## Left to #1187 Detecting that an update exists and the one-click upgrade (with the setting the maintainer decided, enable/disable, off by default) stay in #1187; this issue supplies the manifest it reads. Linked both ways. ## Tests The list shows the three origins; a builtin row has no install or remove button but shows its version; a third-party catalogue is added with a key, an unsigned or wrong-key index is refused with a message; the pinned catalogue cannot be removed; no route installs from outside a manifest; an index close to expiry fails CI; an expired index gives the explicit message. Strings go through Weblate only. Related: #1187, #1252, #1253 to #1262, #729 (the official catalogue), #1170.
tiagoagueda added this to the 0.6.0 milestone 2026-11-13 10:56:29 +00:00
Author
Owner

Related: #1264 (an administrator can hold a plugin or pin it to a version).

Related: #1264 (an administrator can hold a plugin or pin it to a version).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#1263
No description provided.