Fibel

14 · Post

fibel
modell Bestellung
  was   text
  email text
  stand text = "eingegangen"

  post neu an eintrag.email
    betreff "Deine Bestellung: " eintrag.was
    text "Hallo,"
    text "wir haben deine Bestellung erhalten."
    abstand
    ueberschrift "Was du bestellt hast"
    text eintrag.was

  post geaendert an eintrag.email
    betreff "Neuer Stand: " eintrag.stand
    text "Deine Bestellung ist jetzt: " eintrag.stand
    wenn eintrag.stand == "versandt"
      text "Sie ist unterwegs."

Ein post-Block steht im Model, nicht auf einer Seite — denn es geht nicht darum, wo jemand geklickt hat, sondern was mit einem Datensatz geschieht.

post neu an …wenn ein Datensatz angelegt wird
post geaendert an …wenn er geändert wird

Der Datensatz heißt darin eintrag — wie bei sichtbar und aenderbar.

Das Wichtigste zuerst: ohne Zugangsdaten geht nichts hinaus

Solange keine Zugangsdaten in der Umgebung stehen, wird jede Nachricht als Datei abgelegt — in <app>.post/ als .eml, die man mit jedem Mailprogramm öffnen kann.

Das ist die Vorgabe, und zwar mit Absicht. Wer eine App entwickelt und dabei versehentlich echte Post verschickt, tut das an echte Menschen. Wer umgekehrt glaubt zu verschicken und nichts geht hinaus, merkt es an der leeren Ablage. Von beiden Irrtümern ist der zweite der harmlosere.

Wirklich verschicken

Zugangsdaten kommen aus der Umgebung, nie aus der .fibel-Datei. Eine Datei, die man weitergibt und einer KI zu lesen gibt, ist der falsche Ort für ein Passwort.

bash
# über einen Mailserver
FIBEL_POST="smtp://nutzer:geheim@mail.anbieter.de:587"
FIBEL_POST_VON="Karat <keine-antwort@karat.de>"
fibel starte karat

# über einen Anbieter mit HTTP-Schnittstelle
FIBEL_POST="https://api.anbieter.de/senden"
FIBEL_POST_SCHLUESSEL="…"

smtps://… spricht sofort verschlüsselt (Port 465), smtp://… verlangt STARTTLS (Port 587).

Sonderzeichen in Benutzer und Passwort

Zugangsdaten stehen in einer Adresse, und die hat eigene Regeln:

Zeichengeht so?
@ja — getrennt wird am letzten @, ein Postfachname wie mail@domain.de ist also kein Problem
:ja
- . _ ~ ! $ & ' ( ) * + , ; = < > [ ] { }ja, alle unverändert
/ ? # %nein — als %2F, %3F, %23, %25 schreiben

/, ? und # beenden den Teil, in dem sie stehen; ein einzelnes % liest sich als angefangene Kodierung. Vier Zeichen sind es also, und alle anderen kommen unverändert an. Am einfachsten ist ein Passwort, in dem diese vier gar nicht vorkommen — dann stellt sich auch in der Dienstkonfiguration die Frage nach dem verdoppelten % nicht.

Geht es schief, sagt Fibel das — und druckt das Passwort dabei nicht ab, sondern nur die Gestalt der Adresse:

  FIBEL_POST lässt sich nicht lesen: smtp://…@mail.anbieter.de:587
  Vier Zeichen müssen im Passwort umgeschrieben werden:
  / wird %2F, ? wird %3F, # wird %23, % wird %25.
  Alles andere geht unverändert — auch @ und :.

Und noch etwas, das nicht die Adresse betrifft: Das Passwort gehört nicht in die Befehlszeile, denn die Shell schreibt sie in ihre History. Für einen Server gehört es in die Umgebung des Dienstes, nicht in ein Startskript, das jemand weitergeben könnte.

Ohne Verschlüsselung wird nichts verschickt. Bietet der Server kein STARTTLS an, bricht Fibel ab, statt Passwort und Nachricht im Klartext über die Leitung zu schicken. Das ist keine Einstellung.

Prüfen, bevor es darauf ankommt

bash
fibel post karat                        # anmelden und auflegen — verschickt nichts
fibel post karat --an ich@example.de    # genau eine Probe

Ohne --an meldet sich Fibel beim Mailserver an und legt wieder auf. So zeigt sich, ob Adresse, Verschlüsselung und Zugangsdaten stimmen — bevor die erste Bestellbestätigung still im Protokoll hängen bleibt. Gedruckt wird nur, wohin es geht und als wer, nie das Passwort.

Ein frisch angelegtes Postfach oder ein gerade geändertes Passwort kennt mancher Mailserver erst nach ein paar Sekunden. Lehnt fibel post direkt danach ab, versuch es kurz darauf noch einmal, bevor du am Passwort zweifelst.

Auf einem Server

Das Passwort gehört in eine eigene Datei, die nur dir gehört. Der Dienst liest sie ein; so steht es weder in einer Befehlszeile noch in der Datei, die den Dienst beschreibt:

ini
# ~/.config/fibel/post.env   (chmod 600)
FIBEL_POST=smtp://no-reply@beispiel.de:geheim@mail.anbieter.de:587

Mit systemd (etwa auf Uberspace 8) kommt eine kleine Ergänzung neben die Dienstdatei — nicht hinein. Wird die Dienstdatei neu geschrieben, bleibt die Post eingerichtet:

ini
# ~/.config/systemd/user/karat.service.d/post.conf
[Service]
EnvironmentFile=%h/.config/fibel/post.env
Environment="FIBEL_POST_VON=Karat <no-reply@beispiel.de>"

Danach systemctl --user daemon-reload und den Dienst neu starten. Zum Prüfen dieselbe Datei in die Umgebung holen:

bash
(set -a; . ~/.config/fibel/post.env; set +a; fibel post karat)

Bei der HTTP-Schnittstelle geht ein schlichtes JSON hinaus:

json
{ "von": "…", "an": "…", "betreff": "…", "text": "…" }

Ob dein Anbieter genau das erwartet, musst du nachsehen — die Anbieter sind sich darin nicht einig. Zur Not steht ein kleines Zwischenstück davor, das umformt.

Was in einer Nachricht steht

Nur Text. titel, ueberschrift, untertitel, text, hinweis, fett, zitat, linie, abstand — und wenn/sonst, damit man Fälle unterscheiden kann.

Eine karte, ein raster oder ein formular würde in einer Nachricht nichts bedeuten; die Prüfung meldet es als Fehler, statt es stillschweigend wegzulassen. Still verschwinden ist das Schlimmste, was ein Baustein tun kann.

Kein HTML, und das bleibt so: Eine Nachricht, die überall gleich ankommt und die man notfalls im Klartext lesen kann, ist mehr wert als eine, die schön aussieht, solange das Programm mitspielt.

Wann nichts verschickt wird

  • Keine Adresse im Feld. Ein Model, in dem die E-Mail leer bleiben darf, ist kein Grund, eine Bestellung abzubrechen. Es geht dann eben nichts hinaus.
  • Die Adresse enthält kein @. Dasselbe.

Beides ist kein Fehler und hält nichts auf.

Warum das Absenden nicht wartet

Ein Formular darf nicht darauf warten, dass ein Mailserver antwortet. Wer bestellt, soll seine Bestätigungsseite sehen, auch wenn der Anbieter gerade langsam ist.

Deshalb liegen die Nachrichten in einer Schlange und werden der Reihe nach zugestellt. Geht etwas schief, wird es nach 2, 15 und 60 Sekunden erneut versucht; danach steht der Grund im Protokoll des Servers.

Dazu gehört eine Frist: Antwortet der Mailserver 20 Sekunden lang nicht, gilt der Versuch als gescheitert. Ohne sie wäre die stillste aller Fehlerarten möglich — die Gegenstelle legt mitten im Gespräch auf, das Warten auf die nächste Antwort endet nie, und ab da geht gar keine Post mehr hinaus. Ohne Meldung, denn es ist ja nichts schiefgegangen; es ist nur nichts passiert.

Eine Nachricht kommt nicht aus einem Model

Den Code für „Passwort vergessen" verschickt Fibel selbst, sobald am Nutzer-Model ein Feld der Art email steht — dafür braucht es keinen post-Block. Er nimmt denselben Weg und dieselbe Schlange wie alles andere, also gilt auch dort: ohne FIBEL_POST geht er nicht hinaus, sondern in die Ablage. Beschrieben ist das in 05 · Daten und Rechte.

Was noch fehlt

  • Kein Anhang. Eine Rechnung als PDF anzuhängen geht noch nicht.
  • Keine Vorlage über mehrere Models hinweg. Jeder post-Block gehört zu genau einem Model.
  • Keine Abmeldung. Wer keine Post mehr will, kann das nicht selbst einstellen — das ist bei Werbung rechtlich nötig und bei Bestellbestätigungen nicht. Solange Fibel nur das Zweite verschickt, geht es; wer Ersteres vorhat, braucht es.
  • Erst ein echter Anbieter. Gegen einen echten Mailserver (Uberspace) laufen STARTTLS und Anmeldung durch. Das ganze Gespräch bis zur zugestellten Nachricht, mit Umlauten im Betreff, ist bisher nur gegen einen eigenen Testserver geprüft. Nimm die Ablage zum Entwickeln und prüfe den ersten echten Versand mit fibel post … --an.

Zurück zur Übersicht