Architektur
Wie Lictor aufgebaut ist: Anwendungen, die jeweils für sich laufen und skalieren, ein Dienst für die Identität und die Website auf WebGlue. Der Code ist aus den Quellen von 0.2.0 zitiert, wo markiert gekürzt.
Anwendungen, als eines veröffentlicht
Jeder Teil von Lictor — Website, Identität, Moderation, Plattformanbindung, Chatbot und die OAuth2-Autorisierungs- und Token-Server — ist eine OTP-Anwendung mit eigenem Supervision-Baum, eigenen Daten und einer festgelegten Schnittstelle. Sie werden in einem Release ausgeliefert und gemeinsam versioniert; ein Knoten startet die Anwendungen, die seine Rolle nennt, und in der Entwicklung startet ein Knoten alle. Auf Kubernetes ist das ein Image und ein Deployment pro Rolle, jedes mit eigener Replikazahl. Die Knoten bilden einen BEAM-Cluster und sprechen über verteiltes Erlang miteinander, hinter der Schnittstelle der jeweiligen Anwendung.
| Anwendung | Benannt nach | Was sie ist | |
|---|---|---|---|
lictor_elysium | Elysium, die Illusion | Die Website und der Supervision-Baum | seit 0.1 |
lictor_kether | Kether | Identität: Einladungen, Konten, Bestätigung | seit 0.2 |
lictor_geburah | Geburah, der Archont des Gesetzes | Der OAuth2-Autorisierungsserver | geplant, M37 |
lictor_chesed | Chesed, der Archont der Sicherheit | Der OAuth2-Token-Server | geplant, M37 |
Der Supervision-Baum
prx run kompiliert die Suite, baut das Stylesheet, liest die Konfiguration und ruft lictor/main auf, das sie prüft und den Baum startet. In der Entwicklung laufen alle Dienste auf einem Knoten, jeder gestartet, sobald der vorherige läuft (gekürzt):
[(supervisor/child :pg nil {:id :lictor-scope ...}) ; where services are found
(supervisor/child webglue.session.memory nil) ; the sessions
(supervisor/child posthorn.mailbox nil {:id :mailbox ...}) ; the development mailbox
(supervisor/child lictor.kether.service nil {:id :kether ...}) ; identity
(server/child bandit/child (elysium/app) {:port (port)})] ; the siteKether macht sich in einer pg-Gruppe bekannt, sodass die Website es mit lictor.kether/locate genau so findet, wie sie es in einem Cluster täte.
Kether, Identität als Dienst
Kether ist ein Prozess mit einem Speicher, angesprochen über lictor.kether mit seiner Pid. Eine Pid reicht über einen Cluster wie über einen Knoten, deshalb ruft die Website Kether auf dieselbe Weise auf, ob beide einen Knoten teilen oder in eigenen Pods laufen. Die Konten liegen hinter einem Speicher-Protokoll, dessen erste Implementierung im Speicher arbeitet.
(def k (kether/memory-store))
(kether/invite k {:for "Ada" :now now}) ; #(:ok token) -- the link is /invite/<token>
(kether/register k {:invite token :username "ada" :email "ada@example.com"
:password "correct horse battery" :now now
:mailer box :from "lictor@localhost"
:base-url "http://localhost:4000"})
(kether/verify k verify-token now) ; #(:ok "ada")- Ein Token ist die einzige Kopie, die es gibt: Kether behält seinen SHA-256-Hash, sodass eine Kopie des Speichers keine Handvoll gültiger Links ist, und ein zurückgebrachtes Token wird in konstanter Zeit verglichen.
- Ein Passwort wird als
shibboleth-Hash aufbewahrt, PBKDF2-SHA256 mit 600.000 Iterationen. Ein neues Konto bleibt unbestätigt, bis sein gemailter Link benutzt wird. - Die Mail geht zuerst: Eine Mail, die nicht verschickt werden kann, ist eine Registrierung, die nicht stattgefunden hat. Eine Ablehnung behält nichts und lässt die Einladung gültig.
- Nichts in Kether liest die Uhr; jeder Zeitpunkt wird übergeben.
Die Website
Die Website ist eine WebGlue-Anwendung: eine Funktion von einer Request-Map auf eine Response-Map, der Server nur der Weg, auf dem eine Anfrage sie erreicht. Um die Routen liegen, von außen nach innen, statische Dateien, die Session, der Formular-Body und die CSRF-Prüfung:
(let [routed (-> (router/handler (routes))
(csrf/wrap)
(params/wrap)
(session/wrap {:store (sessions/store)})
(in-world world))]
(static/wrap routed {:root (static-root) :at "/assets"}))(elysium/app world) ist die Anwendung über einer Welt — dem Kether-Handle, dem Postfach und einer Uhr — so baut sich ein Test eine, die er steuert. So, wie der Baum sie startet, findet sie die Welt des laufenden Lictor bei jeder Anfrage, sodass ein von seinem Supervisor neu gestartetes Kether gefragt wird.
Jeder Klassenname steht in einem Namespace, lictor.elysium.page: das Layout, eine Karte, eine Überschrift, ein Hinweis, ein Feld, ein Knopf. Eine Seite sagt „ein Hinweis, dass etwas schiefging“; wie er aussieht, wird dort entschieden. Das Aussehen ist daisyUI, mitgeliefert und bei jedem prx compile von Tailwind über webglue_tailwind gebaut, und neben prx run laufend neu gebaut. Zwei Themes stammen aus der Markenpalette, und das Siegel folgt dem Theme.
:prepare ["tailwind build"]
:alongside ["tailwind watch" "posthorn"]
:tailwind {:input "apps/lictor_elysium/assets/lictor.css"
:output "apps/lictor_elysium/priv/static/assets/lictor.css"
:rebuild :always}Plugins in Logos
Plugins werden in Logos geschrieben, der Sprache, mit der Lictors Kunden es erweitern werden. Das macht Logos in Praxis von einer Zugabe zu einer Produktentscheidung: eine Laufzeit statt zwei. Portiert wird nur der Teil von Logos, den die Plugin-Laufzeit braucht, wenn diese Etappe ihn anfordert. Sicherheitsvorkehrungen — begrenzte Ausführung, Ratenlimits und Fehlerisolation — kommen mit dem ersten Plugin, nicht nach dem ersten Vorfall.