A plugin carries a logo — served by Postulo, raster, and not necessarily somebody else's trademark #106
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Depends on
Reference
Postulo/postulo#106
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Observation
Extends #97, which gives a plugin a short name, a full name, an author, a version, a
description and a source link. A logo is the seventh, and unlike the other six it is not a
string -- it needs somewhere to live, a way to be served, and a decision about one format.
It cannot be a static file
Plugins are installed at runtime onto the data volume.
collectstaticran when the imagewas built, and production serves through
CompressedManifestStaticFilesStorage, whichraises on a file the manifest never learned rather than returning a dead link. So a logo
shipped inside a plugin wheel is invisible to the static machinery, always. This is the same
wall #88 hit: static files are served under a content hash, and there is no pattern a name
can be built from.
So it is a view: the bytes come out of the installed package, served by Postulo, under a URL
carrying the plugin's name.
And it cannot be a URL either
jobs/logos.pyhas already settled this for company logos, and the reasoning transfersexactly:
A plugin logo fetched from a vendor's CDN would tell that vendor which instances run their
plugin, how many people use it, and when. Same answer: Postulo serves it or it is not shown.
The format question, which this project has already answered once
Also from
jobs/logos.py:That applies more strongly here, not less. A company logo is fetched from a web page; a
plugin logo arrives inside code an administrator installed, served from Postulo's own origin
under Postulo's own session. An SVG with a script in it, opened directly, is same-origin
script execution -- which is exactly what the content security policy exists to prevent
everywhere else.
Three ways, in order of how much they cost:
whatever metadata the file carried and caps its size. Consistent with the decision
already made, and needs no new thinking. Recommended.
Content-Security-Policy: sandboxand an explicitContent-Typeon the response. Better-looking at every size and, aslogos.pysays,its own step.
Scope
logoon the plugin, beside the identity fields from #97: a filename inside thepackage rather than a path, so nothing a plugin declares can become a path.
available to the person asking, with a cap on bytes and dimensions and a re-encode.
person and for a company with no logo, so nothing is a broken image.
The Europass logo is somebody else's trademark, and that needs deciding rather than
downloading
The Europass brand belongs to the European Union. Two things worth separating:
Displaying it to say "this reads Europass files" is nominative use -- naming a format by
its own mark -- and is what every integration directory does. Uncontroversial.
Shipping the file in this repository is a different statement. Postulo is AGPL-3.0, and
that licence grants rights to the code: it cannot sublicense a mark the project does not
own. Every fork would be redistributing an EU trademark under a licence that has nothing to
say about it. And the Commission's own reuse decision, which makes its documents freely
reusable, excludes logos and trademarks from its scope -- so "it is an EU document" is
not the answer here.
Three options, and this is a decision for the maintainer rather than a detail for whoever
implements it. Not legal advice, just the shape of it:
European Union and is used to identify the format rather than to claim endorsement.
This is exactly what the project already does for artwork it did not write:
src/postulo/static/flags/LICENSE.txtsits beside the flags because MIT asks that thenotice travel with the files. Same shape, different reason.
-- "Europass" -- does the identifying. Costs nothing legally and a little recognisably.
fork rather than for this repository only.
Option 1 is the common practice and option 2 is the one that cannot go wrong. Worth noting
that whichever is chosen sets the precedent for every future plugin: a Paperless logo, a
Telegram logo, a Nextcloud logo. A rule that only works for logos Postulo happens to like is
not a rule.
Classification
Enhancement, interface. Extends #97. The trademark question is not blocking -- a plugin with
no logo must render properly anyway, so the machinery can land before the artwork does.
Decided — option 1
Ship the Europass logo, with its own notice beside it, in its own place: the mark belongs to
the European Union, it is used to identify the format Postulo reads, and no endorsement is
claimed.
The shape already exists in this repository.
src/postulo/static/flags/LICENSE.txtsitsbeside the flag artwork because MIT asks that the notice travel with the files. Same
arrangement, different reason — MIT asks; a trademark is not licensed at all, and the notice
is what makes the use legible rather than what satisfies a licence.
This is a rule, not an exception
Written down now because the next one will not be Europass. It will be Paperless, or
Telegram, or Nextcloud, and a rule that only works for the mark that prompted it is not a
rule.
A plugin may ship a third party's mark when all four hold:
service it connects to. Never decoration, never a badge of quality.
Postulo has opinions about its own palette and none about anybody else's.
claimed or implied.
licence covers the code and cannot speak for somebody else's mark, and every fork
redistributes whatever is in the tree.
Where any of the four fails, the plugin gets the neutral fallback and its name does the work.
What the implementation carries
flags/LICENSE.txtpattern already in the tree.
TRADEMARKS.mdat the root, or a section of the README beside the existing licencenotes — the README already names Lucide (ISC) and flag-icons (MIT), and this is a third
kind of thing: artwork under nobody's licence, used nominatively. It should read
differently from the two above it, because it is different.
One interaction worth catching before it happens
#94 puts an Official badge on plugins whose provenance verifies against a signed index.
#106 puts a vendor's logo on a plugin. Together, on one row, those say something neither
was meant to say: that the vendor has blessed the plugin.
Postulo's own catalogue signature means this file is the one we published. It does not mean
Europass, Telegram or anybody else has looked at it. So the two should not share a visual
frame — a logo is the plugin's identity and a badge is Postulo's claim about provenance, and
they should read as coming from different places. Worth deciding in #94's design rather than
discovering on the page.
The
logofield has been on the manifest since #97 with nothing rendering it. It rendersnow: a plugin names a file inside its own package and Postulo serves it beside the plugin's
name.
All three constraints the issue set out are the ones that shaped it, and each had been
decided once already elsewhere in this codebase:
collectstaticran at buildtime; the manifest storage raises on a file it never learned. The wall #88 hit. So a view.
jobs/logos.pysettled it — an<img>at the plugin author's server wouldtell them which instances run their code, how many people use it and when.
img-src 'self'is the reason, not the obstacle.
what the plugin shipped, not the plugin's file passed through — which also settles "a valid
image with something appended" without having to reason about it.
A name, never a path. A separator or a leading dot is refused rather than normalised, and
importlib.resourcesreads from the package, so nothing a plugin declares can name a fileoutside it. Four parametrised cases hold that.
The path with the most care in it is the one where there is nothing to show, because
that is the ordinary case. No logo, a missing file, an oversized file, an SVG: all four give
the initials tile the interface already uses for a person with no picture and a company with
no logo — one answer to "there is no image here", now in three places — with the reason in
the log rather than a broken image beside a name.
The trademark question, decided
The issue asked for a decision and said it sets the precedent for every future plugin. Option
2: Postulo ships no logo for any plugin of its own, and the rule is in
docs/PLUGINS.mdrather than settled logo by logo.
Displaying a mark to say "this reads Europass files" is nominative use and is not in
question. Redistributing the file under AGPL-3.0 is the different statement: that licence
grants rights to the code and cannot sublicense a mark the project does not own, so every
fork would be redistributing an EU trademark under a licence with nothing to say about it —
and the Commission's reuse decision excludes logos and trademarks from its scope, so "it
is an EU document" is not an answer.
A rule that only works for the marks a project happens to like is not a rule, so it is
written as a rule. Where an owner does permit redistribution, the shape to copy is named:
src/postulo/static/flags/LICENSE.txt, where the notice travels with the files. Option 3 —asking Europass — stays open and would settle it for every downstream fork rather than for
this repository; nothing here forecloses it.
The consequence is that the built-ins show initials tiles today, which is exactly the
fallback the issue said had to work anyway.
Small things
The four failure messages are English and stay English: every one reaches a log line an
administrator reads, none is ever rendered to somebody using Postulo, and thirty-nine
catalogues of them would be work for text no reader sees.
connections:logois recorded intests/test_page_coverage.pyas bytes rather than a page,with the reason — that test caught it, which is what it is for.
15 tests. Shipped in
2325e46on0.3.0, withmainkept level. #94 is the other half ofthe plugins page and is unaffected.