17. Mailinglisten

17.1. Dieses Subsystem ist eine Neuimplementierung von GNU Mailman 3

Vor allem anderen und vor dem ersten Konfigurationsbeispiel muss die Schuld klar benannt werden, denn sie ist groß.

Pepsis Mailinglisten-Subsystem ist eine Neuimplementierung von GNU Mailman 3. Sein Datenmodell, seine Rule/Chain- und Handler/Pipeline-Architektur, seine REST-API, sein E-Mail-Befehlsvokabular und die Namen seiner Benachrichtigungsvorlagen sind der Entwurf des GNU-Mailman-Projekts und nicht unserer. Das GNU-Mailman-Projekt und die Free Software Foundation halten das Urheberrecht am Original. Pepsi existiert, um damit kompatibel zu sein: ein unveränderter mailmanclient, Postorius oder HyperKitty muss diese Implementierung steuern können, und das ist der Abnahmetest, um den der ganze Entwurf herum gebaut ist.

Die Dokumentation von upstream unter <https://docs.mailman3.org> beschreibt Begriffe, die unmittelbar für Pepsis Implementierung gelten, und ist neben diesem Handbuch lesenswert. Wo dieses Kapitel „die hold-Aktion“ oder „die Vorlage list:user:notice:welcome“ sagt, meint es genau das, was Mailman meint.

Einige Dateien von Pepsi haben wir überhaupt nicht geschrieben. Die Benachrichtigungsvorlagen, die Erläuterungen zu den Einstellungen und eine Reihe von Testdaten kommen von GNU Mailman, Postorius und HyperKitty; sie tragen ihre ursprünglichen Urheberrechts- und Lizenzhinweise, sie bleiben unter der GNU General Public License Version 3 oder später, und sie sind in vendor/PEPSI-VENDORING.md aufgeführt. Abschnitt 13 der GPL Version 3 erlaubt es, ein solches Werk mit Pepsis AGPLv3+ zu verbinden; er erlaubt nichts in Richtung einer Umlizenzierung, und Pepsi hat nicht umlizenziert. COPYING sagt dasselbe für jeden, der die Lizenzierung prüft, statt das Handbuch zu lesen.

Warum den Text wiederverwenden, statt eigenen zu schreiben: diese Benachrichtigungen wurden zwei Jahrzehnte lang von echten Listenmitgliedern gelesen, beklagt und ausgebessert, in vierunddreißig Sprachen. Sie von Null an neu zu schreiben wären Wochen Arbeit gewesen, deren bestmögliches Ergebnis darin bestanden hätte, wieder dort anzukommen, wo upstream schon ist, in weniger Sprachen.

17.2. Was eine Liste ist

Eine Mailingliste gehört zu einer Domain und wird auf zwei Weisen bezeichnet, die nicht austauschbar sind:

name@domain

Die Absendeadresse, also die, die Leute eintippen und die im To: erscheint.

name.domain

Die Listen-ID, mit einem Punkt. Das ist, was die REST-API als Ressourcenbezeichner verwendet.

pepsi-list nimmt überall, wo eine Liste benannt wird, beide Schreibweisen an. Der Unterschied zählt, wenn man direkt mit der API spricht, und er ist der von upstream.

Jede Liste antwortet auf neun Adressen: der Absendeadresse selbst und acht Subadressen – -request, -join, -leave, -subscribe, -unsubscribe, -owner, -bounces und -confirm+token. Anders als GNU Mailman erzeugt Pepsi keine Alias-Datei. Mailman muss für diese neun Adressen jeder Liste Postfix- oder Exim-Alias- und Transportmaps schreiben und den MTA zum Neuladen auffordern; Pepsi routet anhand des Empfängers innerhalb der eigenen Pipeline, es gibt also nichts neu zu erzeugen, nichts neu zu laden und nichts, was aus dem Tritt geraten kann.

17.3. Wie das Subsystem aufgebaut ist

Fünf Stages, zwei Bibliotheken und drei Oberflächen. Alles Weitere in diesem Kapitel ist eine Einzelheit an einem davon.

inbound mail
     |
     v
+------------------+   not a list   +----------------------------+
| pepsi-stage-list | -------------> | the rest of your pipeline  |
+------------------+                +----------------------------+
     |         |          |
post |  -owner |  -bounces|
     |  -join  |          |
     |  etc.   |          |
     v         v          v
+----------+ +---------+ +--------+
| ...-post | | command | | bounce |
+----------+ +---------+ +--------+
     |            |          |
     | one row     \        | scores, disables,
     | per member    \      | warns, unsubscribes
     v                v      v
+-------------+   +------------------------+
| ...-deliver |   | replies, notices and   |
+-------------+   | probes, injected at    |
     |            | RESPONSE/NOTICE_STAGE  |
     v            +------------------------+
pepsi-stage-dkim-sign  ->  relay  ->  the member
     |
     +--> the archive (pepsi-archive), and the digest accumulator

Die fünf Stages. pepsi-stage-list routet; sie ist die einzige, die bei jeder Nachricht läuft. pepsi-stage-list-post moderiert eine Listennachricht und verteilt sie mit einem Datensatz pro Mitglied. pepsi-stage-list-deliver macht aus einem dieser Datensätze die Kopie dieses Mitglieds. pepsi-stage-list-command beantwortet alles, was eine Liste per Post tut und was keine Listennachricht ist. pepsi-stage-list-bounce verbraucht die Fehler, die zurückkommen.

Die zwei Bibliotheken. pepsi-list enthält das Listenmodell, die Attributtabelle, die Moderationsentscheidungen, den Bauteil für Sammelnachrichten und die Importer – und ist das, was die Kommandozeile pepsi-list, die REST-API und die Verwalteroberfläche alle aufrufen, sodass die drei Oberflächen sich nicht darüber uneinig sein können, was eine Einstellung bedeutet. pepsi-archive enthält das Archiv: Speicherung, Threading, Suche, Export, Import und Löschung, hinter der Kommandozeile pepsi-archive.

Die drei Oberflächen, jede an einem eigenen Listener-Flag, sind der Grund, weshalb ein einzelner Hostname keine einzelne Vertrauensgrenze ist:

Oberfläche

Listener-Flag

Für wen sie ist

/api/v1, /ui

ADMIN = yes

den Betreiber – pepsi.admin_account, eine Sitzung in pepsi_session; binden Sie es an Loopback. Siehe Die Verwaltungskonsole.

/3.0, /3.1

LIST_API = yes

Programme – mailmanclient, Postorius, HyperKitty, über HTTP Basic. Siehe Die REST-API von GNU Mailman 3.

/lists, /archives

LISTS = yes

Listenverwalter, Moderatoren und Mitglieder – list_user, eine Sitzung in pepsi_list_session. Siehe Die Verwaltungskonsole und Archive.

17.3.1. Drei Identitäten und die Wörter dafür

Das Kapitel benutzt diese drei Wörter in genau diesen Bedeutungen, und die Oberfläche, die Benachrichtigungen und die Handbuchseiten ebenso.

Betreiber

Wer die Pepsi-Installation betreibt. Hält ein pepsi.admin_account, erreicht /ui und /api/v1, bearbeitet pepsi.conf und kann mit jeder Liste alles tun. Es gibt keinen Betreiber je Liste.

Listenverwalter (und Moderator)

Wer eine Liste verwaltet. Hält ein list_user-Konto und einen list_member-Datensatz mit role = owner oder moderator – Befugnis ist eine Abfrage der Mitgliederliste und keine gewährte Berechtigung, was das Modell von Postorius ist und bedeutet, dass einen Verwalter zu ernennen heißt, ihn anzumelden. Ein Verwalter ändert die Einstellungen einer Liste; ein Moderator entscheidet über ihre zurückgehaltenen Nachrichten.

Mitglied

Ein Abonnement, nicht eine Person: eine Adresse auf der Mitgliederliste einer Liste. Wer zwei Adressen auf einer Liste hat, ist zwei Mitglieder, mit zwei Zustellmodi, zwei Bounce-Punkteständen und zwei Abmelde-URIs. Das Wort Abonnent bedeutet in der Prosa dasselbe; Mitglied ist das, was die API, die Oberfläche und dieses Handbuch im Referenzteil verwenden.

Ein Listenverwalter ist kein Pepsi-Betreiber, und die beiden Kontosysteme treffen sich nie; siehe Two account systems unten und Zwei Kontosysteme, und keines gewährt dem anderen etwas für die Tabellen, die Cookies und den einen Hostnamen, der sie verwechseln lässt.

17.3.2. Wie es weitergeht

17.4. Erste Schritte

Eine Domain registrieren, eine Liste anlegen, ihr einen Verwalter geben:

# pepsi-list domain add lists.example.org --base-url https://lists.example.org
added list domain lists.example.org

# pepsi-list list create announce@lists.example.org --style announce-only
created announce@lists.example.org with style legacy-announce

# pepsi-list owner add announce@lists.example.org alice@example.org
alice@example.org is now an owner of announce@lists.example.org

# pepsi-list owner reset-password announce@lists.example.org alice@example.org
password for alice@example.org: xzfkh-9gehq-r7y57-6np8r
(printed rather than mailed: this path has to work when mail is broken)

# pepsi-list check
note: site: archive search: full text and trigram (substring, fuzzy)
1 finding, none fatal

Zwei Warnungen zu diesem Mitschnitt.

Die Domain muss auch eine sein, für die Pepsi Post annimmt. pepsi-list domain add registriert eine Domain für Listen; [pepsi-ingress] ACCEPTED_DOMAINS entscheidet, was der Server bei RCPT annimmt. Eine Liste in einer Domain, die ingress nicht annimmt, wird an der SMTP-Grenze abgewiesen, und nichts in den Protokollen des Listen-Subsystems erwähnt sie, weil die Nachricht es nie erreicht hat.

``members add`` umgeht die Abonnementrichtlinie. Dafür ist ein Kommandozeilenwerkzeug da – eine Mitgliederliste zu migrieren, einen Fehler zu beheben – und es ist auch das eine in diesem Subsystem, das eine Mailingliste in eine Spamkanone verwandeln kann. Der Bestätigungsablauf existiert, damit eine Adresse belegt, dass sie dort sein will; die einzigen Wege, die ihn überspringen, sind dieser Befehl und ein ausdrücklich pre_verified + pre_confirmed gestellter Aufruf eines authentifizierten REST-Clients.

17.5. Stile

Ein Stil ist ein benannter Satz von Attributvorgaben, der beim Anlegen einer Liste angewandt wird. GET /3.0/lists/styles meldet drei, die von upstream sind, mit den Namen, Beschreibungen und der Voreinstellung von upstream:

Name

Auch angenommen als

Was er ist

legacy-default

discussion, default

Gewöhnliche Diskussionsliste. Die Voreinstellung.

legacy-announce

announce-only, announce

Nur Ankündigungen: Mitglieder dürfen nicht senden.

private-default

private

Diskussionsliste mit privatem Archiv; nicht beworben, und Abonnements brauchen sowohl eine Bestätigung als auch einen Moderator.

Ein weiterer Stil ist Pepsis eigener. Er wird von pepsi-list und von der Verwalteroberfläche angenommen und nicht über REST aufgeführt:

moderated

Eine Diskussionsliste, auf der jede Nachricht eines Mitglieds für einen Moderator zurückgehalten wird.

Ein unbekannter Stilname ist ein Fehler. Das create_list von upstream wendet stillschweigend überhaupt keinen Stil an, wenn es einen Namen nicht erkennt, was eine Liste hinterlässt, deren Attribute alle auf dem Spaltenstandard stehen, ohne dass irgendwo etwas gesagt wird; das Bereitstellungsskript eines migrierenden Servers kann auf diese Weise Monate lang scheitern.

17.6. Einstellungen je Liste

Eine Liste trägt die 99 Attribute, die GNU Mailman 3.3.10 über REST offenlegt. pepsi-list list show gibt sie aus; --explain ergänzt zu jedem eine Zeile Prosa:

$ pepsi-list list show announce@lists.example.org | head -6
accept_these_nonmembers
acceptable_aliases
admin_immed_notify                         yes
admin_notify_mchanges                      no
administrivia                              yes
advertised                                 yes

Siebzehn von ihnen sind nur lesbar, darunter die acht abgeleiteten Adressen (posting_address, owner_address, bounces_address und die übrigen), die aus der Identität der Liste berechnet und nie gespeichert werden – eine zweite Kopie der Identität einer Liste ist eine zweite Stelle, an der sie falsch sein kann.

Sechs weitere, die *_uri-Vorlagenattribute, existieren nur in API-Version 3.0; Version 3.1 erreicht sie stattdessen über die Vorlagenverwaltung. list show markiert beide Arten.

Warnung

``unsubscription_policy`` regiert den Ein-Klick-Abmeldelink nicht. Wenn [pepsi-list] BASE_URL und UNSUBSCRIBE_SECRET beide gesetzt sind, trägt jede Nachricht, die Pepsi an eine Liste sendet, einen List-Unsubscribe-Post-Header nach RFC 8058, und die „Abmelden“-Schaltfläche eines Mailbox-Anbieters ruft dieses Ziel mit einem einzigen POST auf. Ein gültiges Token entfernt das Mitglied unmittelbar, was auch immer dieses Attribut sagt — moderate und confirm eingeschlossen.

Das ist beabsichtigt und keine Lücke. RFC 8058 erlaubt keine weitere Interaktion: ein Anbieter, der eine Umleitung, eine Bestätigungsseite oder ein „Ihre Anfrage wartet auf Genehmigung“ erhält, kann mit keinem davon etwas anfangen, und viele markieren die Post stattdessen einfach als Spam. Ein Header, der einen Klick verspricht und ihn dann nicht einhält, ist schlimmer als keiner.

Die Richtlinie regiert weiterhin alles, was ein Mensch tut: die -leave-Adresse, das Abmeldeformular auf der Seite der Liste und das DELETE über REST. Muss eine Liste wirklich jeden Abgang moderieren, dann lautet die Antwort, den Header nicht mehr auszugeben — lassen Sie [pepsi-list] UNSUBSCRIBE_SECRET ungesetzt, und es wird überhaupt keine https:-Form ausgegeben — und hinzunehmen, dass die Liste bei jedem prüfenden Mailbox-Anbieter schlechter dasteht.

17.6.1. Drei Einstellungen, nach denen häufiger gefragt wird als nach den anderen sechsundneunzig

advertised

Ob die Liste im öffentlichen Verzeichnis erscheint. Es ist keine Datenschutzeinstellung: eine nicht beworbene Liste ist weiterhin über ihre eigene URL erreichbar, nimmt weiterhin Nachrichten an, und ihr Archiv wird von archive_policy regiert und nicht hiervon. Das ist die Bedeutung bei upstream, und sie zu ändern wäre ein Kompatibilitätsbruch. „Nicht beworben“ klingt wie „versteckt“; es bedeutet „nicht im Verzeichnis“.

archive_policy

never, private oder public. never speichert nichts – das Archiv wird nicht geschrieben, es gibt also später nichts öffentlich zu machen. private speichert die Nachrichten und weist anonyme Leser ab: die Archiv-URLs antworten, als gäbe es die Liste nicht, denn ein „verboten“ würde bestätigen, dass es sie gibt. public ist öffentlich für das Internet, Suchmaschinen eingeschlossen, und /robots.txt bittet sie, das Archiv zu indizieren und den Suchendpunkt in Ruhe zu lassen.

member_roster_visibility

Wer sehen darf, wer angemeldet ist: public, members oder moderators. Die eigene Seite der Liste zeigt die Mitgliederliste nur bei public. Dies ist das Attribut, nach dem man greift, wenn jemand „nicht beworben“ sagt und meint „die Abonnenten sollen kein öffentliches Verzeichnis derer sein, die mit uns korrespondieren“.

Alle neunundneunzig liegen auf elf Seiten der Verwalteroberfläche, in der Gruppierung von Postorius, und die Seiten sind aus derselben Deklaration erzeugt, die list show und die REST-API antreibt – ein Wert, den die Oberfläche ablehnt, wird von beiden anderen abgelehnt, mit denselben Worten. Zwei Kontosysteme, und keines gewährt dem anderen etwas hat die Rundschau.

17.6.2. Einstellungen, die Pepsi erfunden hat

pepsi-list list set-ext verwaltet einen zweiten, viel kleineren Satz von Einstellungen je Liste, die Pepsi hinzugefügt hat und die die REST-API nie zeigt:

search_trigram

off, short oder full – wie weit der Index dieser Liste für Teilzeichenfolgen- und Fuzzy-Suche reicht. Auf die serverweite Obergrenze SEARCH_TRIGRAM beschnitten.

archive_show_addresses

Anonymen Lesern des öffentlichen Archivs dieser Liste die vollständigen E-Mail-Adressen der Absender zeigen. Standardmäßig aus.

archive_retention_days

Archivierte Nachrichten löschen, die älter als dies sind. Fehlt der Wert, gilt der Serverstandard.

Sie sind für REST absichtlich unsichtbar, und das ist keine Stilfrage. Das PUT /3.0/lists/<id>/config von upstream über die ganze Ressource verlangt, dass jedes schreibbare Attribut in der Anfrage vorkommt. Ein gegen Mailman 3.3.10 gebauter Client weiß von nichts, was Pepsi hinzugefügt hat, würde unsere also auslassen und abgewiesen werden – ein erfundenes Attribut würde jedes solche PUT von jedem bestehenden Client brechen. Pepsis eigene Einstellungen liegen daher ganz außerhalb des Attributsatzes, erreichbar von der Kommandozeile und der Verwalteroberfläche und von nirgends, wo ein Mailman-Client sie sehen kann.

17.7. Konfiguration

Die Konfiguration je Liste liegt in der Datenbank. pepsi.conf enthält nur das, was der Betreiber besitzt:

[pepsi-list]
SITE_OWNER = postmaster@example.org
BASE_URL = https://lists.example.org
API_USER = restadmin
API_PASS = a long random string
SEARCH_TRIGRAM = short
ARCHIVE_PARTITIONS = 32
NOTICE_STAGE = dkim-sign

Den vollständigen Satz zeigt pepsi.conf(5). Drei davon verdienen hier eine Anmerkung.

NOTICE_STAGE ist die Stelle, an der die periodischen Durchläufe ihre Post ablegen. Die meisten Benachrichtigungen sendet eine Stage, die ein eigenes RESPONSE_STAGE hat; die Bounce-Durchläufe zum Warnen und Entfernen laufen von einem Timer, der keines hat. Ohne diese Option können diese Durchläufe den Zustand eines Mitglieds ändern, es aber nicht benachrichtigen, daher verweigert pepsi-setup eine Konfiguration, die Bounces ohne sie verarbeitet.

ARCHIVE_PARTITIONS wird bei der Installation festgelegt. Das Archiv ist nach der Liste hash-partitioniert, und PostgreSQL kann nicht im Betrieb neu partitionieren: den Modulus zu ändern bedeutet eine neue Tabelle und eine vollständige Kopie jeder archivierten Nachricht. pepsi-setup legt die Partitionen einmal an und weigert sich, sie danach stillschweigend zu ändern, und pepsi-list check meldet eine Konfiguration, die dem widerspricht, was auf der Platte liegt. Wählen Sie einmal. Zweiunddreißig ist bequem weit jenseits des Punktes, an dem etwas anderes zum Engpass wird.

SEARCH_TRIGRAM ist eine Obergrenze und kein Standardwert. Jede Liste wählt off, short oder full bis zu ihr. Den Serverwert zu senken verkleinert daher den Index beim nächsten Neuaufbau und betrifft nicht nur später angelegte Listen.

17.8. Die Teilzeichenfolgensuche ist optional

Pepsis Archivsuche benutzt zwei Mechanismen von PostgreSQL, und sie beantworten verschiedene Fragen. Die Volltextsuche – Stammformen, Gewichtung, Auszüge – beantwortet „finde mir den Thread über Zertifikate“. Die Trigrammsuche aus der Erweiterung pg_trgm beantwortet, worin Volltext schlecht ist: eine Teilzeichenfolge, einen Schreibfehler, einen Hostnamen, einen Konfigurationsschlüssel, eine Zeile aus einem Traceback. Auf einer technischen Mailingliste ist das ein großer Teil dessen, wonach Leute tatsächlich suchen, und ein Stammform-Wörterbuch wirft genau dieses Material weg.

pg_trgm liegt in postgresql-contrib, das minimale Container-Abbilder oft auslassen. Ein Server ohne es installiert dennoch und funktioniert dennoch; er hat einfach nur Volltextsuche. pepsi-list check sagt, in welchem Modus der Server ist:

$ pepsi-list check
warning: site: archive search: full text only -- pg_trgm is not installed,
         so substring and fuzzy search are unavailable
    install postgresql-contrib and re-run pepsi-setup; the archive then
    needs `pepsi-archive reindex` to populate trgm_text

17.9. Zwei Kontosysteme

Listenverwalter sind keine Pepsi-Betreiber, und die beiden Identitäten treffen sich nie.

pepsi.admin_account und die Konsole /ui gehören dem Serverbetreiber, und das Handbuch sagt, sie an Loopback zu binden und über einen SSH-Tunnel zu erreichen. Listenverwalter können offensichtlich nicht so arbeiten. Die Verwalteroberfläche liegt daher auf dem öffentlichen Listener und leitet ihre Befugnis aus der Mitgliederliste her – ein Datensatz in list_member mit role = owner oder moderator –, was genau das Modell von Postorius ist, bei dem Autorisierung eine Abfrage der Mitgliederliste und keine gewährte Berechtigung ist.

Zwei Kontosysteme, mit Absicht, mit getrennten Tabellen, getrennten Sitzungscookies und getrennten Passwort-Hashes, und keines gewährt dem anderen etwas. Eine Mitgliedssitzung kann einen administrativen Wächter nie befriedigen, in keiner Richtung. Das zählt mehr, als es aussieht: Cookies sind an den Host gebunden und nicht an den Listener, ein Betreiber, der beide Oberflächen unter einem Hostnamen anbietet, verlässt sich daher allein auf die Cookienamen und die Prüfungen des Wächters.

Zwei Kontosysteme, und keines gewährt dem anderen etwas stellt die beiden nebeneinander, benennt die Tabellen und Cookies und sagt, was passiert, wenn Sie beide unter einem Hostnamen anbieten. Ein Verwalter, der sucht, was er tatsächlich tun kann, will die Darstellung der Konsole im selben Kapitel.

17.10. Bounce-Verarbeitung

Eine Mailingliste sendet an Adressen, die aufhören zu funktionieren, und irgendwer muss es bemerken. Pepsis Antwort ist die von GNU Mailman 3, Schritt für Schritt, weil jeder Knopf darin über die REST-API sichtbar ist und die Dokumentation eines Servers weiterhin wahr bleiben muss.

Die Gestalt davon: ein Zustellfehler kommt an die -bounces-Adresse der Liste zurück, er wird dem betroffenen Mitglied zugeordnet, der Bounce-Punktestand dieses Mitglieds steigt um höchstens eins pro Kalendertag, und wenn der Punktestand die Schwelle der Liste erreicht, wird seine Zustellung abgeschaltet. Abgeschaltet zu sein ist nicht abgemeldet zu sein – ein abgeschaltetes Mitglied wird in den folgenden Wochen einige Male gewarnt und erst dann entfernt.

17.10.1. Die Zuordnung ist kein Raten

Jede Kopie jeder Listennachricht, die Pepsi versendet, trägt einen Umschlagabsender, der das Mitglied benennt, für das sie ist: announce-bounces+alice=example.net@lists.example.org. Ein Bounce dieser Kopie sagt daher, um wen es geht, ohne dass irgendetwas den Bounce selbst lesen muss. Mailman kann das nur, wenn ein Betreiber VERP einschaltet; Pepsis Fan-out erfolgt immer je Empfänger, es ist daher nie aus. Das + ist der [pepsi] RECIPIENT_DELIMITER der Installation — die eine Option, mit der der Fan-out schreibt und an der der Router trennt, sodass die beiden nicht voneinander abweichen können.

Das zählt aus einem praktischen Grund. Die siebzehn heuristischen Bounce-Erkenner von upstream – einer nach einer Norm (RFC 3464) und sechzehn, die einen Hersteller oder einen Betrieb benennen – existieren, weil die Zuordnung über den Umschlag nicht verfügbar war. Pepsi portiert alle siebzehn (sie sind die von flufl.bounce, Apache-2.0, mit ihren eigenen Testdaten), aber sie lesen immer nur einen Bounce, der auf anderem Weg ankam. Wie oft das geschieht, wird in pepsi.event_log unter list.bounce.unrecognized gezählt, und diese Zahl ist die ehrliche Antwort auf „braucht dieser Server mehr Erkenner“.

Ein Bounce, den nichts erkennt, erreicht einen Menschen, mit dem Original wörtlich beigefügt, sofern das forward_unrecognized_bounces_to der Liste nicht discard sagt. Diese Weiterleitung ist beabsichtigt: das Beispiel ist der einzige Weg, auf dem der nächste Erkenner geschrieben wird.

17.10.2. Die neun Attribute

Attribut

Was es entscheidet

process_bounces

Ob diese Liste Bounces überhaupt bewertet. Aus bedeutet, dass sie ungelesen verworfen werden.

bounce_score_threshold

Wie viele Bewertungstage ein Mitglied abschalten (Standard 5).

bounce_info_stale_after

Wie lange ein Punktestand ohne neuen Bounce überlebt (Standard 7 Tage). Danach beginnt der nächste Bounce wieder bei 1.

bounce_you_are_disabled_warnings

Wie viele Warnungen ein abgeschaltetes Mitglied vor der Entfernung erhält (Standard 3). Null bedeutet Entfernung ohne jede Warnung.

bounce_you_are_disabled_warnings_interval

Wie lange zwischen diesen Warnungen (Standard 7 Tage).

bounce_notify_owner_on_bounce_increment

Die Verwalter über jede Erhöhung benachrichtigen (Standard aus – auf einer großen Liste ist das viel Post).

bounce_notify_owner_on_disable

Die Verwalter benachrichtigen, wenn ein Mitglied abgeschaltet wird (Standard an).

bounce_notify_owner_on_removal

Die Verwalter benachrichtigen, wenn ein Mitglied entfernt wird (Standard an).

forward_unrecognized_bounces_to

administrators (Standard), site_owner oder discard.

17.10.3. Ein durchgearbeitetes Beispiel

Eine Liste mit den Voreinstellungen und eine Adresse, die keine Post mehr annimmt:

Tag 1

ein dauerhafter Fehler kommt an. Punktestand 1. Niemand wird benachrichtigt.

Tag 1

vier weitere Fehler am selben Tag. Weiterhin Punktestand 1 – höchstens eine Erhöhung pro Kalendertag.

Tage 2–4

je ein Fehler pro Tag. Punktestand 4.

Tag 5

noch einer. Punktestand 5 = die Schwelle: die Zustellung wird abgeschaltet und die Verwalter werden benachrichtigt, mit der auslösenden Zustellungsstatusbenachrichtigung als Anhang.

Tag 5

das Mitglied erhält eine Warnung, die sagt, dass sein Abonnement abgeschaltet wurde und warum. Gesendete Warnungen: 1.

Tag 12

Warnung 2.

Tag 19

Warnung 3.

Tag 26

die Warnungen sind aufgebraucht und das Intervall ist erneut verstrichen: das Mitglied wird abgemeldet und die Verwalter werden benachrichtigt.

Eine Adresse, die stirbt, braucht also etwa vier Wochen, um die Liste zu verlassen, und ein schlechter Nachmittag bei einem Anbieter kostet einen Punkt. Ein Bounce-Sturm – tausend Fehler in einer Stunde – kostet ebenfalls genau einen Punkt.

Alles nach „abgeschaltet“ erledigt pepsi-list tasks --once, was ein Timer täglich ausführt. Führt es nichts aus, werden Mitglieder abgeschaltet und dann nie gewarnt und nie entfernt.

17.10.4. Proben

[pepsi-list] BOUNCE_PROBES = yes ändert, was das Überschreiten der Schwelle tut: statt das Mitglied abzuschalten, sendet Pepsi ihm eine Nachricht mit einem Einmal-Token im Umschlagabsender und setzt seinen Punktestand auf null. Kommt diese Nachricht als Bounce zurück, benennt das Token es genau und es wird sofort abgeschaltet. Kommt sie nicht zurück, war alles in Ordnung und nichts geschieht.

Standardmäßig ist es aus, wie bei upstream. Der Handel lautet: eine weitere Nachricht an eine Adresse, die vermutlich tot ist, gegen das Nicht-Abschalten von jemandem, dessen Anbieter einen schlechten Nachmittag hatte.

17.10.5. Was eine Migration nicht mitbringt

Ein von Mailman importierter Server beginnt bei jedem Mitglied mit Punktestand null. Der eigene Importer von upstream liest die Bounce-Geschichte ebenfalls nicht, das stimmt also überein – es bedeutet aber, dass eine lange tote Adresse nach der Umstellung noch einen Zustellversuch erhält.

17.11. Bekannte Einschränkungen

Das Subsystem umfasst das Datenmodell und das Kommandozeilenwerkzeug, den Posting-Pfad mit seiner Moderations-Chain und dem Fan-out je Mitglied, die E-Mail-Befehlsschnittstelle, die Bounce-Verarbeitung, das Archiv und seine Suche (Archive), die REST-API von GNU Mailman 3 (Die REST-API von GNU Mailman 3), die öffentliche Weboberfläche, die Mitgliedskonten und die Verwalteroberfläche (Die Verwaltungskonsole), die Importer (Installation) und beide Sammelnachrichtenformate.

Was es nicht tut: /plugins ist immer leer und es gibt keine Plugin-Schnittstelle, es gibt kein NNTP-Gateway (gateway_to_news ist setzbar und wird als die Leeroperation protokolliert, die es ist), Thread-Verschlagwortung und Kategorien fehlen im Archiv, und eine zurückgewiesene zurückgehaltene Nachricht wird ohne die Zurückweisungsbenachrichtigung verworfen, die upstream sendet. Jedes davon wird noch einmal dort gesagt, wo ein Leser darauf trifft.

17.12. Sammelnachrichten

Beide Formate, MIME multipart/digest und die einfache Form nach RFC 1153, denn ein Client kann digest_is_default und mime_is_default_digest zurücklesen und ein Mitglied kann plaintext_digests verlangen – ein Format auszuliefern und das Attribut für das andere anzunehmen wäre genau die stille Art Lüge, deren Verhinderung der Kompatibilitätsvertrag dient.

17.12.1. Wie eine Sammelnachricht anwächst und wie sie gesendet wird

Das Anwachsen ist Teil des Zustellens einer Listennachricht: ist digests_enabled an, hängt der Posting-Pfad die Nachricht an einen Datensatz je Liste an, innerhalb derselben Transaktion wie alles andere, was die Nachricht tut. Upstream hängt unter einer Sperre an eine MMDF-Mailboxdatei an; ein Datensatz ist eine der Stellen, an denen eine Datenbank einfach besser ist.

Der Datensatz trägt die Nachrichten selbst und keine Verweise auf ein Archiv. Eine Liste darf durchaus digests_enabled mit archive_policy = never haben – pepsi-list check weist sogar darauf hin, dass Sammelnachrichten dann die einzige Aufzeichnung des Gesendeten sind –, eine aus Archivverweisen gebaute Sammelnachricht wäre also genau für die Listen leer, die sie am dringendsten brauchen. digest_size_threshold begrenzt, wie viel anwächst.

Das Senden hat zwei Auslöser, und upstream hat überhaupt keinen periodischen Läufer: sein maybe_send_digest_now feuert synchron bei einer Listennachricht, sobald die Größenschwelle überschritten ist, und mailman digests --send soll in den Cron des Betreibers wandern. Pepsi behält beide Hälften und setzt die zweite dorthin, wo sie hingehört:

  • Größe – digest_size_threshold, in Kilobyte (die Einheit von upstream, die der Spaltenname nicht nennt). Null bedeutet, dass die Größe nie ein Senden auslöst, eine Liste kann also rein periodisch sein.

  • periodisch – pepsi-list digest periodic von einem systemd-Timer, unter Beachtung von digest_send_periodic je Liste. Dieselbe Anordnung wie bei pepsi-list tasks und der Grund, weshalb kein langlebiger Sammelnachrichtendienst geschrieben ist.

pepsi-list digest send erzwingt eine Ausgabe unabhängig von der Schwelle, und digest bump erhöht den Band ohne zu senden – die beiden Flags von upstream.

17.12.2. Band- und Ausgabennummer

digest_volume_frequency (yearly, monthly, quarterly, weekly, daily) entscheidet, ob der Zeitraum seit digest_last_sent_at fortgeschritten ist:

  • eine Liste, die noch nie eine Sammelnachricht gesendet hat, behält ihren aktuellen Band und ihre aktuelle Nummer, die erste Ausgabe einer Liste ist also Ausgabe 1 und nicht Ausgabe 2;

  • ist der Zeitraum fortgeschritten, erhöht sich der Band und die Nummer wird auf 1 zurückgesetzt;

  • ist er es nicht, erhöht sich die Nummer.

Verglichen wird der Zeitraum und nicht die verstrichene Zeit, und genau das macht die Antwort am 1. eines Monats gleich der am 31.: zwei Nachrichten mit einem Tag Abstand über eine Monatsgrenze hinweg liegen in verschiedenen Monaten, und zwei Nachrichten mit dreißig Tagen Abstand innerhalb eines Monats nicht.

17.12.3. Wie die beiden Formate aussehen

Die MIME-Sammelnachricht ist ein multipart/mixed aus fünf Teilen in dieser Reihenfolge: der Kopf (der den Betreff der Ausgabe als seine Content-Description trägt, was eine Sammelnachricht in einem Client lesbar macht, der Teile auflistet), die Kopfzeile, das Inhaltsverzeichnis, ein inneres multipart/digest, dessen Kinder die Originalnachrichten als message/rfc822 sind, und die Fußzeile. Die Teile für Kopf- und Fußzeile entfallen, wenn ihre Vorlage leer ist – was list:member:digest:header ist, bei upstream und hier.

Die Sammelnachricht nach RFC 1153 ist ein flacher text/plain-Text, und jede Anzahl darin ist tragend, weil Leser Sammelnachrichten seit 1990 an diesen Zeilen aufteilen: siebzig Bindestriche einmal nach dem Inhaltsverzeichnis, dreißig vor jeder Nachricht außer der ersten, die Fußzeile als vorgetäuschte zusätzliche Nachricht mit ihrem eigenen Subject: Digest Footer statt als Anhängsel, und ein Schlussgruß, gefolgt von einer Zeile Sternchen derselben Länge.

RFC 1153 ist reiner Text, ein Anhang kann das also nicht überleben. Jeder nicht-textuelle Teil wird daher zu einem Platzhalter, der benennt, was entfernt wurde – sein Dateiname, sein Medientyp und seine Größe –, denn eine einfache Sammelnachricht, die stillschweigend eine Tabelle fallen ließe, würde einen Leser im Glauben lassen, er habe die ganze Nachricht gesehen. Ein multipart/alternative liefert seinen Klartext und sagt nichts über den HTML-Zwilling, denn es wurde nichts weggenommen.

17.12.4. Wer welche erhält

Sammelnachrichtenmitglieder mit aktivierter Zustellung teilen sich nach delivery_mode: plaintext_digests erhalten die Ausgabe nach RFC 1153, und mime_digests und ``summary_digests`` erhalten beide die MIME-Ausgabe. Upstream hat kein eigenes Summenformat und behandelt die beiden gleich; eines zu erfinden würde bedeuten, dass ein Mitglied ein Format verlangen kann, das kein anderes Mailman erzeugt.

Jedes Format wird einmal gebaut und je Empfänger verteilt, zu denselben Bedingungen wie jede andere Zustellung – eine Sammelnachricht trägt ebenfalls ein List-Unsubscribe.

17.13. Erklärte Unterschiede zu GNU Mailman 3

An einer Stelle gesammelt, weil ein Betreiber, der die beiden Systeme abwägt, es an einer Stelle braucht und ein Supportgespräch etwas braucht, worauf es zeigen kann. Jeder davon ist eine Entscheidung mit einem Grund und kein Zufall.

Unterschied

Warum

Ein-Klick-Abmeldung nach RFC 8058

Wir geben List-Unsubscribe-Post aus und upstream nicht, und ein gültiges Token entfernt das Mitglied unmittelbar, was auch immer ``unsubscription_policy`` sagt. RFC 8058 erlaubt keine weitere Interaktion, und ein Header, der einen Klick verspricht und ihn dann nicht einhält, ist schlimmer als kein Header.

Das Fan-out erfolgt immer je Empfänger

personalize steuert, ob die Nachricht für jedes Mitglied umgeschrieben wird, nicht, ob jedes Mitglied seinen eigenen Datensatz erhält – denn jede Kopie trägt die eigene Abmelde-URI dieses Mitglieds.

Archivierte Absenderadressen werden verschleiert

Für anonyme Betrachter, standardmäßig, mit einer Überschreibung je Liste. Keine Sicherheitsmaßnahme: verhindert wird die Masseneinsammlung, die ein öffentliches Archiv zu einer Spamquelle macht. Ein angemeldetes Mitglied sieht die ganze Adresse.

Eine Löschung hinterlässt einen 90-Tage-Grabstein

Damit eine erneute Zustellung oder ein erneuter Import eine absichtlich gelöschte Nachricht nicht wiederauferstehen lässt. --forget überspringt ihn.

``max_days_to_hold`` wird beachtet

Es ist bei upstream wirkungslos: ein grep über den ganzen Nicht-Test-Baum von Mailman 3 findet die Spalte, den Stilstandard und den REST-Prüfer und keinen Job, der es liest. Hier verwirft pepsi-list tasks --once zurückgehaltene Nachrichten jenseits der Grenze, protokolliert jede davon und nennt den Moderatoren eine Anzahl. Eine strikte Obermenge – die Voreinstellung 0 bedeutet „läuft nie ab“.

Ein unbekannter ``style_name`` ist ein Fehler

Das create_list von upstream schlägt den Stil nach, erhält nichts und legt die Liste ohne jeden angewandten Stil an – jedes Attribut auf dem Spaltenstandard, kein Fehler irgendwo. Ein Bereitstellungsskript mit einem Tippfehler würde stillschweigend Listen ohne die beabsichtigten Vorgaben erzeugen. Wir verweigern, und das kann keinen Client brechen, der einen Namen übergibt, den er aus /lists/styles gelesen hat.

Vorlagen-URIs werden unter Grenzen abgerufen

Ein Listenverwalter — nicht der Betreiber — kann eine setzen, daher wird eine file:-URI auf jedem Schreibweg abgelehnt und nie gelesen, Größe und Zeit sind begrenzt, und Umleitungen sind eingeschränkt. Upstream hat solche Grenzen nicht.

``archive_rendering_mode = markdown`` wird gespeichert und nicht gerendert

Das Attribut existiert, weil der Attributsatz der Vertrag ist; beide Werte werden als Text dargestellt. Vom Benutzer geliefertes Markdown in unseren eigenen Origin zu rendern ist genau das, was die Content-Security-Policy der Seiten unmöglich machen soll.

Nirgends JavaScript

Auf keiner der beiden Browseroberflächen. Der Preis sind die interaktiven Teile von HyperKitty; der Gewinn ist eine Richtlinie überhaupt ohne script-src.

``X-Mailman-*`` ist zu ``X-Pepsi-List-*`` umbenannt

Mit einer Zuordnungstabelle, weil procmail- und Sieve-Regeln die echten Kosten eines migrierenden Benutzers sind.

Kein NNTP-Gateway, keine einsteckbaren Archivierer, keine Plugin-API

Die sechs NNTP-Attribute bleiben in der Attributmenge und sind wirkungslos, und gateway_to_news wird protokolliert, wenn es gegriffen hätte, statt ignoriert zu werden — eine stille Leeroperation würde einen Betreiber in dem Glauben lassen, sein Gateway funktioniere.

Passwörter, Bounce-Punktestände und Token in Bearbeitung werden nicht migriert

Der eigene Importer von upstream bringt sie ebenfalls nicht mit. Siehe Von GNU Mailman migrieren.