Say where each plugin came from: shipped, official, or somebody's upload #94

Closed
opened 2026-09-07 15:19:02 +00:00 by tiagoagueda · 2 comments
Owner

Observation

admin plugin page:
repos: internal (cannot be disable), oficial (can be enable/disable and customized, if it
is not on the env, like smtp), multiple repos that can be enable/disable/customized

plugins kinds: internal (shipped with postulo), oficial plugin (downloaded from official
repo, or thru a zip) custom plugin (downloaded from custom repo, or thru a zip)
all plugins can be enable and disable but a admin can impose enable/disable of a plugin on
any user

user-plugin:
can see what current plugins (either kind) are enable in their session

Second of four. Depends on the repositories issue, which is where "official" comes from.

What exists

installing.Installed records an origin:

origin: str = "upload"  # upload | catalogue:<name>

So a plugin knows whether it was uploaded or came from a named catalogue, and
is_from_catalogue is already a property. Two gaps:

The built-in plugins are not in the record at all. BUILTIN_SOURCES -- the schema.org
and page-metadata readers -- are Python classes in the image. They never pass through
installing.py, so Server settings -> Plugins lists what an administrator installed and
not what the instance can actually do. Somebody looking at that page to answer "can this
instance read a posting off a page" is looking in the wrong place.

An upload has no provenance. The request asks for an official plugin to be recognisable
whether it came "from official repo, or thru a zip", and that second half cannot be taken at
face value: a zip is a zip. A file somebody uploads carries no evidence of who published
it, and labelling it official because it was named after an official plugin would be worse
than not labelling it at all.

There is an honest way to do it, and it uses what the catalogue already computes. A signed
index lists each wheel's SHA-256. So when a wheel is uploaded, hash it and look for that hash
in the indexes of the enabled repositories. If it matches, that file is byte-for-byte the
one the official repository signed
, and it can be labelled official truthfully -- the
signature covers the bytes, not the delivery. If it matches nothing, the label is "uploaded",
which is the whole truth about it.

What this asks for

Provenance derived rather than declared, shown on the page and available to the rest of the
interface:

Kind Where it came from Removable
Internal Shipped inside Postulo No
Official The official repository, or a file whose checksum that repository signed Yes
Custom A custom repository, or a file matching nothing Yes

Worth being careful about

The label must describe evidence, not intent. "Official" has to mean "a signature was
checked", never "it was called official". If verification is not available at the moment of
upload -- the repository is disabled, or its index cannot be fetched -- the answer is
"uploaded", not a guess.

Provenance is not safety and the page must not imply it is. The install page already
says that installing a plugin runs somebody else's code inside Postulo, and a badge that
reads Official is exactly the kind of thing that quietly undoes a warning. Whatever the
wording, it should not end up meaning "safe".

Dependencies stay unverified whatever the badge says. installing.py is explicit that
the signature and checksum cover the plugin's own wheel and that its requirements come from
PyPI at install time. An official plugin's dependencies are as unverified as anybody's, and
the badge must not appear to cover them.

Scope

  • A provenance derived from the record and the enabled repositories' signed indexes.
  • Built-in plugins listed alongside installed ones, marked internal and not removable.
  • Upload verified against the indexes by checksum, and honest when it matches nothing.
  • Tests: an upload matching an official checksum is official; the same file with one byte
    changed is not; a built-in cannot be removed or uninstalled through any route.

Classification

Enhancement, interface.

## Observation > admin plugin page: > repos: internal (cannot be disable), oficial (can be enable/disable and customized, if it > is not on the env, like smtp), multiple repos that can be enable/disable/customized > > plugins kinds: internal (shipped with postulo), oficial plugin (downloaded from official > repo, or thru a zip) custom plugin (downloaded from custom repo, or thru a zip) > all plugins can be enable and disable but a admin can impose enable/disable of a plugin on > any user > > user-plugin: > can see what current plugins (either kind) are enable in their session Second of four. Depends on the repositories issue, which is where "official" comes from. ## What exists `installing.Installed` records an origin: ```python origin: str = "upload" # upload | catalogue:<name> ``` So a plugin knows whether it was uploaded or came from a named catalogue, and `is_from_catalogue` is already a property. Two gaps: **The built-in plugins are not in the record at all.** `BUILTIN_SOURCES` -- the schema.org and page-metadata readers -- are Python classes in the image. They never pass through `installing.py`, so *Server settings -> Plugins* lists what an administrator installed and not what the instance can actually do. Somebody looking at that page to answer "can this instance read a posting off a page" is looking in the wrong place. **An upload has no provenance.** The request asks for an official plugin to be recognisable whether it came "from official repo, or thru a zip", and that second half cannot be taken at face value: **a zip is a zip**. A file somebody uploads carries no evidence of who published it, and labelling it official because it was named after an official plugin would be worse than not labelling it at all. There is an honest way to do it, and it uses what the catalogue already computes. A signed index lists each wheel's SHA-256. So when a wheel is uploaded, hash it and look for that hash in the indexes of the enabled repositories. **If it matches, that file is byte-for-byte the one the official repository signed**, and it can be labelled official truthfully -- the signature covers the bytes, not the delivery. If it matches nothing, the label is "uploaded", which is the whole truth about it. ## What this asks for Provenance derived rather than declared, shown on the page and available to the rest of the interface: | Kind | Where it came from | Removable | | --- | --- | --- | | Internal | Shipped inside Postulo | No | | Official | The official repository, or a file whose checksum that repository signed | Yes | | Custom | A custom repository, or a file matching nothing | Yes | ## Worth being careful about **The label must describe evidence, not intent.** "Official" has to mean "a signature was checked", never "it was called official". If verification is not available at the moment of upload -- the repository is disabled, or its index cannot be fetched -- the answer is "uploaded", not a guess. **Provenance is not safety and the page must not imply it is.** The install page already says that installing a plugin runs somebody else's code inside Postulo, and a badge that reads *Official* is exactly the kind of thing that quietly undoes a warning. Whatever the wording, it should not end up meaning "safe". **Dependencies stay unverified whatever the badge says.** `installing.py` is explicit that the signature and checksum cover the plugin's own wheel and that its requirements come from PyPI at install time. An official plugin's dependencies are as unverified as anybody's, and the badge must not appear to cover them. ## Scope - A provenance derived from the record and the enabled repositories' signed indexes. - Built-in plugins listed alongside installed ones, marked internal and not removable. - Upload verified against the indexes by checksum, and honest when it matches nothing. - Tests: an upload matching an official checksum is official; the same file with one byte changed is not; a built-in cannot be removed or uninstalled through any route. ## Classification Enhancement, interface.
tiagoagueda added this to the 0.3.0 milestone 2026-09-07 15:19:02 +00:00
Author
Owner

#106 is decided: a plugin may ship a third party's mark with a notice beside it, unmodified,
outside the AGPL grant, claiming no endorsement.

Relevant here because of the Official badge this issue introduces. A vendor's logo and a
Postulo provenance badge on the same row read together as "the vendor blessed this plugin",
which is not what a catalogue signature means — it means this file is the one we published.
Worth keeping the logo and the badge visually separate, so identity and provenance do not
appear to be the same claim. Detail is in #106.

#106 is decided: a plugin may ship a third party's mark with a notice beside it, unmodified, outside the AGPL grant, claiming no endorsement. Relevant here because of the **Official** badge this issue introduces. A vendor's logo and a Postulo provenance badge on the same row read together as "the vendor blessed this plugin", which is not what a catalogue signature means — it means *this file is the one we published*. Worth keeping the logo and the badge visually separate, so identity and provenance do not appear to be the same claim. Detail is in #106.
Author
Owner

Every item in the scope, and the sentence the whole thing turns on is the issue's own: a
zip is a zip.

The page lists what the instance can do

The two built-in sources are Python classes in the image and never pass through
installing.py, so Server settings → Plugins listed everything except them — and somebody
opening it to answer "can this instance read a posting off a page" was looking in the wrong
place. All seven built-ins are listed now, marked Internal, with no button that could
not work
: a built-in has no line in the record for Remove or Switch off to act on, and the
policy rows above are how it is switched off for people. Refused at installing.remove() and
installing.set_disabled() too, so a management command or a shell gets the same answer with
the same reason.

The record and the built-ins come back from one function in one shape now, so the page, the
plugins command and whatever reads it next work on both without knowing there are two.

Provenance is derived, and describes evidence

A signed index publishes each release's SHA-256, and installing.py records the digest of
every wheel it installs. So an upload is checked against what the enabled repositories
currently sign: a match means the bytes on this instance are the bytes that repository
published, whatever route they took, because a signature covers bytes and not delivery. One
byte of difference and it is not — that pair of tests is the point of doing it this way.

Which repository is official is a key, not a name. Anybody can call theirs postulo;
nobody else can sign with Postulo's key. OFFICIAL_KEYS is empty, because Postulo
publishes no catalogue yet — so nothing is official on any instance today and everything is
custom or uploaded. That is the truthful thing for the page to say rather than a placeholder,
and it changes by adding a key rather than by writing the module again.

The three warnings

"The label must describe evidence, not intent." It does, and the case you singled out is
tested by name: with no repository reachable — switched off, unreachable, an index that will
not verify — the answer is Uploaded, not the last answer and not a guess from the name.

"Provenance is not safety and the page must not imply it is." A test asserts that the
page still carries its warning about running somebody else's code, and that no sentence
beside a badge contains the word safe. The explanations talk about a file matching a
signature, which is all they know.

"Dependencies stay unverified whatever the badge says." A test asserts that none of the
four sentences mentions them at all — the safest way for a badge not to appear to cover
something is for it not to speak about it.

16 tests, 7 new strings in all 39 European catalogues, and a section in docs/PLUGINS.md.

Shipped in 809f620 on 0.3.0, with main kept level. Third of the four in that series;
#95 and #96 are the ones about deciding per person, and are unaffected.

Every item in the scope, and the sentence the whole thing turns on is the issue's own: **a zip is a zip.** ## The page lists what the instance can do The two built-in sources are Python classes in the image and never pass through `installing.py`, so *Server settings → Plugins* listed everything except them — and somebody opening it to answer "can this instance read a posting off a page" was looking in the wrong place. All seven built-ins are listed now, marked **Internal**, with **no button that could not work**: a built-in has no line in the record for Remove or Switch off to act on, and the policy rows above are how it is switched off for people. Refused at `installing.remove()` and `installing.set_disabled()` too, so a management command or a shell gets the same answer with the same reason. The record and the built-ins come back from one function in one shape now, so the page, the `plugins` command and whatever reads it next work on both without knowing there are two. ## Provenance is derived, and describes evidence A signed index publishes each release's SHA-256, and `installing.py` records the digest of every wheel it installs. So an upload is checked against what the enabled repositories **currently** sign: a match means the bytes on this instance are the bytes that repository published, whatever route they took, because a signature covers bytes and not delivery. One byte of difference and it is not — that pair of tests is the point of doing it this way. **Which repository is official is a key, not a name.** Anybody can call theirs `postulo`; nobody else can sign with Postulo's key. `OFFICIAL_KEYS` is **empty**, because Postulo publishes no catalogue yet — so nothing is official on any instance today and everything is custom or uploaded. That is the truthful thing for the page to say rather than a placeholder, and it changes by adding a key rather than by writing the module again. ## The three warnings **"The label must describe evidence, not intent."** It does, and the case you singled out is tested by name: with no repository reachable — switched off, unreachable, an index that will not verify — the answer is **Uploaded**, not the last answer and not a guess from the name. **"Provenance is not safety and the page must not imply it is."** A test asserts that the page still carries its warning about running somebody else's code, and that no sentence beside a badge contains the word *safe*. The explanations talk about a file matching a signature, which is all they know. **"Dependencies stay unverified whatever the badge says."** A test asserts that none of the four sentences mentions them at all — the safest way for a badge not to appear to cover something is for it not to speak about it. 16 tests, 7 new strings in all 39 European catalogues, and a section in `docs/PLUGINS.md`. Shipped in `809f620` on `0.3.0`, with `main` kept level. Third of the four in that series; #95 and #96 are the ones about deciding per person, and are unaffected.
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.

Reference
Postulo/postulo#94
No description provided.