Modul-Architektur-Handbuch v1.1
Wie Prilog-Module gebaut werden. Der offizielle Standard für interne und externe Modul-Entwicklung, den Prilog Store und die Abrechnungs-Integration.
Version 1.1 · März 2026 · Prilog GmbH · info@prilog.chat
| Status | Zielgruppe | Referenz |
|---|---|---|
| Implementiert | Entwickler & Architekten | prilog.chat/docs/modules |
Implementierungsstatus (Stand: März 2026)
Dieses Handbuch ist vollständig implementiert. Alle beschriebenen Komponenten existieren als funktionsfähiger Code:
- Core-Infrastruktur: Event-Bus, Lifecycle-Manager, Scoped Contexts →
prilog-backend-api/src/core/ - Platform-API: Modul-Lifecycle-Endpunkte →
prilog-backend-api/src/routes/platform-v1/module-lifecycle.router.ts - Store: Katalog, Einreichung, Review →
prilog-backend-api/src/routes/store/ - Billing: Stripe-Integration für alle 3 Modelle →
prilog-backend-api/src/core/billing/ - Monitoring: Performance-Metriken, Health-Dashboard, Audit-Logging →
prilog-backend-api/src/core/monitoring/ - SDK:
@prilog/types,@prilog/sdk,@prilog/create-module,@prilog/module-validator→prilog-sdk/ - Frontend: Dynamisches Module Loading →
prilog-web-client/src/core/module-registry/ - Portal: Store-UI →
prilog-portal/src/app/modules/store/ - Proof of Concept: Projekt-Modul (Typ B) extrahiert →
prilog-backend-api/src/modules/project/ - Test-Modul: Ankündigungen (Typ A) →
prilog-ankuendigungen/
Siehe Umsetzungsbericht für Details.
1. Vision
Prilog ist eine Multi-Tenant-Kommunikationsplattform für Schulen. Der Kern — Matrix/Synapse-Server, Nutzer-Verwaltung, Space-Struktur, Chat — bleibt schlank, stabil und schnell. Alles, was darüber hinausgeht, lebt in Modulen.
Module sind eigenständige, abgegrenzte Erweiterungen. Sie können von Prilog selbst, von Partnerunternehmen oder von externen Entwicklern gebaut und im Prilog Store angeboten werden. Ein Modul kann kostenlos, mietbar oder nutzungsbasiert abgerechnet werden.
Das Ziel ist ein lebendiges Ökosystem: Schulen buchen was sie brauchen. Entwickler bauen was fehlt. Prilog stellt die Plattform, die Regeln und die Infrastruktur zur Verfügung.
Leitprinzip
- Ein Modul darf den Core niemals verlangsamen, destabilisieren oder verändern.
- Ein Modul läuft immer tenant-isoliert — es kann niemals auf Daten einer anderen Schule zugreifen.
- Ein Modul kommuniziert mit dem Core ausschließlich über definierte Schnittstellen.
- Ein Modul kann jederzeit aktiviert und deaktiviert werden — ohne Datenverlust, ohne Systemausfall.
2. Grundbegriffe
| Begriff | Definition |
|---|---|
| Core | Das unveränderliche Fundament von Prilog: Matrix/Synapse-Server, Authentifizierung, Nutzer- und Space-Verwaltung, Platform-API, Billing-Grundstruktur. Der Core wird nie durch ein Modul verändert. |
| Modul | Eine eigenständige, abgegrenzte Funktionalitätserweiterung. Lebt vollständig außerhalb des Core. Kommuniziert nur über offizielle Schnittstellen. Kann aktiviert, deaktiviert, gemietet und bewertet werden. |
| Tenant | Eine Schule bzw. ein Schulserver in Prilog. Technisch: ein Eintrag in server_orders mit einer eindeutigen server_order_id. Jeder Tenant hat eigene Nutzer, eigene Konfiguration und vollständig isolierte Daten. |
| Prilog Store | Der Marktplatz für Prilog-Module. Ähnlich dem WordPress Plugin Directory. Schulen entdecken, buchen und verwalten Module. Entwickler veröffentlichen und monetarisieren ihre Module. |
| Modul-SDK | Das offizielle Entwicklerwerkzeug von Prilog. Stellt Typen, Hilfsfunktionen, den Event-Bus-Client und den AuthContext zur Verfügung. Externe Entwickler bauen ausschließlich gegen das SDK. |
| Event-Bus | Das interne Kommunikationssystem von Prilog. Module hören auf Events (z.B. "neuer Termin") und senden Events (z.B. "Benachrichtigung"). Keine direkte Kommunikation zwischen Modulen oder mit Core-Datenbanktabellen. |
| Feature-Flag | Ein boolescher Schalter im Bootstrap-Endpunkt der Platform-API. Zeigt dem Frontend an, welche Module für diesen Tenant aktiv sind. Frontends rendern Modul-Bereiche nur wenn der Flag gesetzt ist. |
| AuthContext | Das standardisierte Objekt das bei jedem API-Request verfügbar ist. Enthält: tenant (server_order_id), userId, systemRole, aktive Spaces. Jede Modul-Route erhält diesen Kontext automatisch. |
| Manifest | Eine JSON-Datei (prilog-module.json) die ein Modul beschreibt: Name, Version, Abhängigkeiten, benötigte Berechtigungen, Abrechnungsmodell, Store-Metadaten. |
3. Architektur-Übersicht
Prilog ist in drei konzentrische Ringe aufgeteilt. Die Grenze zwischen den Ringen ist verbindlich — kein Code darf sie durchbrechen.
┌─────────────────────────────────────────────────────┐
│ Ring 3 – Externe Module (Prilog Store) │
│ Von Partnerunternehmen und unabhängigen Entwicklern │
│ Immer gegen das SDK gebaut │
│ ┌─────────────────────────────────────────────┐ │
│ │ Ring 2 – Offizielle Prilog-Module │ │
│ │ Advanced Calendar · Projekt-Modul │ │
│ │ SpaceMaker · SIS-Integration · weitere │ │
│ │ ┌─────────────────────────────────────┐ │ │
│ │ │ Ring 1 – Core (unveränderlich) │ │ │
│ │ │ Matrix/Synapse · Nutzer & Auth │ │ │
│ │ │ Space/Raum-Struktur · Platform-API │ │ │
│ │ │ Billing-Basis · Event-Bus │ │ │
│ │ └─────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘Die drei Ringe kommunizieren ausschließlich nach innen: Ring 3 spricht mit Ring 2 über das SDK. Ring 2 spricht mit Ring 1 über die Platform-API und den Event-Bus. Ring 1 kennt die äußeren Ringe nicht — er weiß nur, welche Module aktiv sind.
3.1 Was der Core bereitstellt
| Schnittstelle | Beschreibung |
|---|---|
| Platform-API | Versionierte REST-Endpunkte (/api/platform/v1/*). Authentifizierung, Nutzer- und Space-Abfragen, Feature-Flags, Bootstrap. Einziger erlaubter Zugangspunkt für Modul-Backend-Code zum Core. |
| Event-Bus | Internes Pub/Sub-System. Module subscriben auf Core-Events und publizieren eigene Events. Keine direkte Kopplung zwischen Produzent und Konsument. |
| AuthContext | Wird bei jedem Request automatisch aufgelöst und dem Modul übergeben. Enthält tenant, userId, roles. Module müssen Auth nie selbst implementieren. |
| Modul-Registry | Die zentrale Datenbank-Tabelle module_registrations. Hier sind alle bekannten Module mit Manifest, Version und Status eingetragen. |
| Billing-Hook | Ein standardisierter Callback-Punkt. Module melden Nutzungsereignisse (Events, aktivierte Tenants, Dateivolumen) an den Core. Der Core leitet es an die Billing-API weiter. |
| Store-API | Die Schnittstelle zum Prilog Store: Modul suchen, Metadaten lesen, Bewertungen abfragen, Installationen zählen. |
4. Modul-Typen
Module werden nach ihrer Komplexität und ihren Integrationspunkten klassifiziert. Die Klassifizierung bestimmt den Review-Aufwand im Store und die erlaubten Schnittstellen.
| Typ | Beschreibung | Erlaubte Schnittstellen | Review-Aufwand |
|---|---|---|---|
| Typ A – UI-Erweiterung | Fügt neue Ansichten oder Widgets im Frontend hinzu. Keine eigene Backend-Logik, keine eigene DB. | Platform-API (read-only), Feature-Flags | Gering |
| Typ B – Backend-Modul | Eigene API-Routen, eigene DB-Tabellen, eigene Business-Logik. Kein Zugriff auf Core-Tabellen. | Platform-API, Event-Bus, eigene DB-Tabellen, Billing-Hook | Mittel |
| Typ C – Integration | Verbindet Prilog mit externen Diensten (z.B. Schulverwaltungssysteme, Kalender-Apps, LDAP). | Platform-API, Event-Bus, externe APIs, Billing-Hook | Hoch |
| Typ D – Core-nahe Erweiterung | Erweitert die Matrix/Synapse-Schicht (Application Services). Nur für Prilog-interne Entwicklung. | Alle Schnittstellen + Synapse Admin API | Sehr hoch (intern) |
Hinweis für externe Entwickler
Externe Entwickler können ausschließlich Module vom Typ A, B und C bauen. Typ D ist für den Prilog-Kern reserviert und wird im Store nicht akzeptiert. Der Modul-Typ wird im Manifest deklariert und beim Review verifiziert.
5. Das Modul-Manifest
Jedes Modul enthält eine Datei prilog-module.json im Root-Verzeichnis. Das Manifest ist der Ausweis des Moduls — es beschreibt alles was Prilog wissen muss: Identität, Abhängigkeiten, Berechtigungen, Abrechnung und Store-Metadaten.
5.1 Vollständiges Manifest-Schema
| Feld | Pflicht | Beschreibung |
|---|---|---|
id | Ja | Eindeutiger Bezeichner. Format: prilog-{name}. Nur Kleinbuchstaben, Bindestriche. Unveränderlich nach Veröffentlichung. |
name | Ja | Anzeigename im Store. Max. 60 Zeichen. |
version | Ja | Semantic Versioning: MAJOR.MINOR.PATCH |
type | Ja | A | B | C | D — Modul-Typ (siehe Kapitel 4) |
prilogCoreVersion | Ja | Minimale Core-Version. Format: >=1.2.0. Modul wird nicht installiert wenn Core älter ist. |
description | Ja | Kurzbeschreibung (max. 200 Zeichen). Erscheint im Store. |
longDescription | Nein | Ausführliche Beschreibung mit Markdown. Erscheint auf der Store-Detailseite. |
author | Ja | Name und E-Mail des Entwicklers oder der Organisation. |
license | Ja | SPDX-Lizenzkennung oder "proprietary". |
permissions | Ja | Array der angeforderten Berechtigungen (siehe Kapitel 6). |
billing | Nein | Abrechnungs-Konfiguration (siehe Kapitel 11). Fehlt = kostenlos. |
migrations | Nein | Pfad zu DB-Migrations-Dateien. Nur Typ B und C. |
routes | Nein | Pfad zur Route-Registrierungsdatei. Nur Typ B und C. |
eventSubscriptions | Nein | Liste der Core-Events auf die das Modul hört. |
featureFlag | Ja | Name des Feature-Flags im Bootstrap-Response. z.B. "calendar" |
storeCategory | Nein | Kategorie im Store: communication | organization | analytics | integration | other |
screenshots | Nein | Array von Screenshot-URLs für die Store-Seite. |
supportUrl | Nein | URL zum Support-Kanal des Entwicklers. |
documentationUrl | Nein | URL zur Modul-Dokumentation. |
5.2 Beispiel-Manifest
{
"id": "prilog-advanced-calendar",
"name": "Advanced Calendar",
"version": "1.0.0",
"type": "B",
"prilogCoreVersion": ">=1.0.0",
"description": "Multilayer-Kalender mit Space-Integration und iCal-Sync.",
"author": { "name": "Prilog GmbH", "email": "info@prilog.chat" },
"license": "proprietary",
"permissions": ["calendar:read", "calendar:write", "events:publish"],
"billing": {
"model": "subscription",
"monthlyPrice": 4.90,
"currency": "EUR",
"trialDays": 30
},
"migrations": "./prisma/migrations/calendar/",
"routes": "./src/routes/index.ts",
"eventSubscriptions": ["space.created", "user.joined"],
"featureFlag": "calendar",
"storeCategory": "organization"
}6. Berechtigungs-System
Jedes Modul deklariert im Manifest welche Berechtigungen es benötigt. Der Tenant-Admin stimmt diesen Berechtigungen bei der Installation zu — analog zu App-Berechtigungen auf einem Smartphone. Nicht angeforderte Berechtigungen sind immer verweigert.
6.1 Verfügbare Berechtigungen
| Berechtigung | Zugriff auf | Typ |
|---|---|---|
users:read | Nutzerliste des Tenants lesen (ohne Passwörter, ohne private Daten) | Lesend |
spaces:read | Space- und Raum-Struktur des Tenants lesen | Lesend |
spaces:write | Spaces und Räume erstellen und bearbeiten | Schreibend |
messages:read | Chat-Nachrichten lesen (nur in freigegebenen Räumen) | Lesend |
events:publish | Events auf dem Event-Bus veröffentlichen | Schreibend |
events:subscribe | Auf Core-Events hören | Lesend |
files:read | Dateien aus dem Prilog-Dateispeicher lesen | Lesend |
files:write | Dateien in den Prilog-Dateispeicher schreiben | Schreibend |
billing:report | Nutzungsereignisse an die Billing-API melden | System |
notifications:send | Benachrichtigungen an Nutzer senden (über Core-Benachrichtigungsdienst) | Schreibend |
external:http | HTTP-Anfragen an externe URLs senden (nur freigegebene Domains) | Extern |
Sicherheitshinweis
- Der Core prüft bei jedem API-Request ob das Modul die benötigte Berechtigung besitzt. Fehlt eine Berechtigung → HTTP 403, kein Fallback, keine stille Ablehnung.
external:httperfordert zusätzlich eine Whitelist der erlaubten Domains im Manifest.- Berechtigungen können nach der Veröffentlichung nicht ohne neue Modul-Version erweitert werden.
7. Modul-Struktur & Dateiaufbau
Alle Module — intern und extern — folgen derselben Verzeichnisstruktur. Diese Einheitlichkeit ermöglicht automatisches Review, Deployment und Monitoring.
7.1 Verzeichnisstruktur
| Pfad | Inhalt & Zweck |
|---|---|
prilog-module.json | Pflicht. Das Manifest (siehe Kapitel 5). |
README.md | Pflicht. Entwickler-Dokumentation. Wird auf der Store-Seite angezeigt. |
CHANGELOG.md | Pflicht ab v1.0. Versionshistorie mit Breaking Changes. |
src/ | Gesamter Quellcode des Moduls. |
src/index.ts | Pflicht. Modul-Einstiegspunkt. Registriert Routes, Events, Migrations. |
src/routes/ | API-Endpunkte des Moduls. Konvention: eine Datei pro Ressource. |
src/services/ | Business-Logik. Kein direkter DB-Zugriff in Routes — immer Services. |
src/events/ | Event-Handler und Event-Publisher. |
src/types.ts | TypeScript-Typen und Interfaces. Exportiert für SDK-Konsumenten. |
prisma/schema.module.prisma | DB-Schema des Moduls. Wird in den Prilog-Prisma-Context eingebunden. |
prisma/migrations/ | DB-Migrations. Werden beim Aktivieren automatisch ausgeführt. |
tests/ | Unit- und Integrationstests. Mindestabdeckung: 70%. |
dist/ | Kompilierter JavaScript-Output. Wird für den Store-Upload verwendet. |
7.2 Der Modul-Einstiegspunkt
Die Datei src/index.ts ist der einzige Ort wo ein Modul sich beim Core registriert. Sie exportiert eine Funktion register() die beim Aktivieren des Moduls aufgerufen wird.
// src/index.ts
import type { PrilogModuleContext } from "@prilog/sdk";
export async function register(ctx: PrilogModuleContext): Promise<void> {
// 1. Routen registrieren
await ctx.routes.register(import("./routes"));
// 2. Event-Subscriptions
ctx.events.on("space.created", import("./events/onSpaceCreated"));
ctx.events.on("user.joined", import("./events/onUserJoined"));
// 3. DB-Migrations ausführen (automatisch beim ersten Aktivieren)
await ctx.db.migrate();
// 4. Feature-Flag aktivieren
ctx.featureFlags.enable("calendar");
}
export async function unregister(ctx: PrilogModuleContext): Promise<void> {
ctx.featureFlags.disable("calendar");
// Daten bleiben 30 Tage erhalten.
}8. Modul-Lifecycle
Jedes Modul durchläuft einen definierten Lebenszyklus — von der Entwicklung bis zur Deaktivierung. Der Core steuert jeden Übergang und stellt sicher dass kein Zustand inkonsistent bleibt.
| # | Status | Was passiert | Rückgängig? |
|---|---|---|---|
| 1 | Entwicklung | Entwickler baut Modul lokal gegen Prilog-Dev-Server. SDK stellt Test-Fixtures für Core-Events bereit. | n/a |
| 2 | Review | Modul wird bei Prilog eingereicht. Automatische Sicherheits- und Kompatibilitätsprüfung. Manuelle Code-Review für Typ C und D. | Immer |
| 3 | Im Store | Modul ist im Prilog Store verfügbar. Noch nicht bei Tenants aktiv. | Jederzeit zurückziehen |
| 4 | Installiert | Tenant-Admin installiert Modul. Manifest wird validiert. Berechtigungen werden angezeigt und bestätigt. | Deinstallieren |
| 5 | Migrations | DB-Migrations werden für diesen Tenant ausgeführt. Bei Fehler: automatischer Rollback, Modul bleibt inaktiv. | Automatischer Rollback |
| 6 | Aktiv | Routen werden registriert. Feature-Flag gesetzt. Event-Subscriptions aktiv. Abrechnung startet. | Deaktivieren |
| 7 | Deaktiviert | Routen werden deregistriert. Feature-Flag gelöscht. Events werden nicht mehr verarbeitet. Abrechnung stoppt. | Reaktivieren |
| 8 | Daten-Frist | 30-Tage-Frist nach Deaktivierung. Modul-Daten bleiben erhalten. Reaktivierung stellt vollen Zustand wieder her. | Reaktivieren innerhalb 30 Tage |
| 9 | Gelöscht | Modul-Daten werden nach 30 Tagen unwiderruflich gelöscht. Schema-Migrations werden rückgebaut. | Nicht möglich |
8.1 Drei-Stufen-Aktivierung (Wer macht was?)
Der Lifecycle beschreibt die technischen Zustaende. In der Praxis gibt es drei menschliche Akteure, die jeweils eine Stufe steuern:
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ STUFE 1: Admin │ │ STUFE 2: Portal │ │ STUFE 3: Client │
│ (Prilog-intern) │────>│ (Kunden-Admin) │────>│ (Endnutzer) │
│ │ │ │ │ │
│ Modul global │ │ Modul fuer diesen │ │ Modul ist sichtbar │
│ freigeben/sperren │ │ Server aktivieren │ │ und nutzbar │
│ │ │ │ │ │
│ Admin-Panel: │ │ Portal: │ │ Web-Client: │
│ /modules/registry │ │ /modules/store │ │ Feature-Flags │
└─────────────────────┘ └─────────────────────┘ └─────────────────────┘| Stufe | Wer | Was | Wo | Technisch |
|---|---|---|---|---|
| 1 | Prilog-Admin | Modul in der Registry freigeben | Admin-Panel → Modul-Registry | PATCH /admin/module-registry/:moduleId setzt ModuleRegistration.status auf active |
| 2 | Kunden-Admin (Schule) | Modul installieren und aktivieren | Portal → Modul-Store | POST /modules/install + POST /modules/installed/:id/activate erzeugt TenantModuleInstallation |
| 3 | Endnutzer | Modul nutzen | Web-Client | GET /bootstrap liefert Feature-Flags, Frontend zeigt nur aktive Module |
Stufe 1 — Admin-Panel (Prilog-intern):
- Der Prilog-Admin sieht alle Module in der
ModuleRegistrationTabelle - Er kann den Status auf
active,inactive,deprecatedoderremovedsetzen - Nur Module mit Status
activeerscheinen im Portal-Store (Stufe 2) - Wenn ein Modul deaktiviert wird, koennen keine neuen Installationen mehr erfolgen
Stufe 2 — Portal (Kunden-Admin):
- Der Kunden-Admin sieht nur Module die in Stufe 1 freigegeben wurden
- Er installiert das Modul →
TenantModuleInstallationmit Statusinstalled - Er aktiviert das Modul → Status
active, Feature-Flag wird gesetzt - Er kann deaktivieren → Status
deactivated, 30-Tage Daten-Aufbewahrung
Stufe 3 — Web-Client (Endnutzer):
- Der Bootstrap-Endpoint prueft
TenantModuleInstallationmit Statusactive - Fuer jedes aktive Modul wird ein Feature-Flag gesetzt
- Die Frontend-Module-Registry rendert nur Routen fuer aktivierte Module
- Endnutzer sehen das Modul in der Navigation und koennen es nutzen
Wichtig
Stufe 1 ist ein globaler Schalter. Wenn ein Modul dort deaktiviert wird, bleibt es fuer bestehende Kunden aktiv (ihre TenantModuleInstallation aendert sich nicht), aber neue Installationen sind nicht mehr moeglich.
9. Integrations-Schnittstellen
Module kommunizieren mit dem Core ausschließlich über vier definierte Schnittstellen. Direkter Zugriff auf Core-Datenbanktabellen oder Core-internen Code ist verboten und technisch nicht möglich.
9.1 Platform-API
Die Platform-API ist die primäre Schnittstelle für Abfragen von Core-Daten. Alle Endpunkte sind versioniert und erfordern einen gültigen AuthContext.
| Endpunkt | Beschreibung |
|---|---|
GET /api/platform/v1/bootstrap | Feature-Flags, Modul-Konfiguration, Nutzerrolle für diesen Tenant. Wird beim App-Start vom Frontend abgerufen. |
GET /api/platform/v1/users | Nutzerliste des Tenants. Erfordert Berechtigung users:read. |
GET /api/platform/v1/spaces | Space- und Raum-Struktur. Erfordert spaces:read. |
GET /api/platform/v1/me | Profil des aktuellen Nutzers. Immer verfügbar. |
POST /api/platform/v1/notify | Benachrichtigung an einen oder mehrere Nutzer senden. Erfordert notifications:send. |
9.2 Event-Bus
Der Event-Bus ermöglicht lose Kopplung zwischen Core und Modulen sowie zwischen Modulen untereinander. Kein Modul ruft ein anderes direkt auf.
Core-Events (read-only für Module)
| Event | Beschreibung |
|---|---|
user.created | Ein neuer Nutzer wurde angelegt. Payload: { userId, tenantId, role } |
user.joined | Nutzer ist einem Space beigetreten. Payload: { userId, spaceId, tenantId } |
user.left | Nutzer hat einen Space verlassen. |
space.created | Ein neuer Space wurde erstellt. Payload: { spaceId, tenantId, name } |
space.deleted | Ein Space wurde gelöscht. |
room.created | Ein neuer Raum wurde in einem Space erstellt. |
module.activated | Ein Modul wurde für einen Tenant aktiviert. Payload: { moduleId, tenantId } |
module.deactivated | Ein Modul wurde für einen Tenant deaktiviert. |
billing.period.end | Abrechnungszeitraum endet in 7 Tagen. Payload: { tenantId, periodEnd } |
Modul-Events
Module können eigene Events publizieren. Andere Module können diese abonnieren — vorausgesetzt beide sind für denselben Tenant aktiv. Cross-Tenant-Events existieren nicht.
Event-Namen folgen dem Format: {moduleId}.{ressource}.{aktion} — Beispiel: prilog-calendar.event.created
9.3 Datenbank-Zugriff
Typ-B- und Typ-C-Module erhalten einen eigenen Prisma-Client-Scope der ausschließlich auf die Tabellen des Moduls zugreift. Core-Tabellen sind nicht referenzierbar.
// Erlaubt: Modul-eigene Tabelle
const events = await ctx.db.calendarEvent.findMany({
where: { tenantId: ctx.auth.tenantId }
// tenantId IMMER Pflicht
});
// Verboten: Core-Tabelle — führt zu Compile-Fehler
// ctx.db.serverOrder.findMany(...) → TypeError: serverOrder is undefined9.4 Billing-Hook
Module mit Abrechnungsmodell melden Nutzungsereignisse über den standardisierten Billing-Hook an den Core.
| Methode | Beschreibung |
|---|---|
ctx.billing.reportUsage(event) | Meldet ein einzelnes Nutzungsereignis. Payload: { type, quantity, tenantId, timestamp } |
ctx.billing.reportActive() | Meldet dass das Modul für diesen Tenant in diesem Abrechnungszeitraum aktiv war. Für Subscription-Modelle. |
ctx.billing.getUsage() | Liest die bisher gemeldete Nutzung für diesen Tenant im aktuellen Zeitraum. |
10. Tenant-Fähigkeit
Tenant-Fähigkeit ist keine Option — sie ist eine Grundvoraussetzung. Jedes Modul muss von der ersten Codezeile an multi-tenant-fähig sein. Diese Regeln werden beim Review automatisch geprüft.
| # | Regel | Verbindliche Anforderung |
|---|---|---|
| 1 | Datenisolation | Jede DB-Tabelle enthält tenantId als Pflicht-Feld. Jede Query enthält WHERE tenantId = ctx.auth.tenantId. Ohne diesen Filter: Compile-Fehler durch den SDK-Linter. |
| 2 | Aktivierung pro Tenant | Das Modul kann für Tenant A aktiv und für Tenant B inaktiv sein. Status, Konfiguration und Daten sind immer pro Tenant getrennt. |
| 3 | Konfiguration pro Tenant | Modul-Einstellungen werden in module_config(tenantId, moduleId, key, value) gespeichert. Kein globaler Konfigurations-State. |
| 4 | Rate-Limits pro Tenant | API-Limits und Ressourcengrenzen gelten pro Tenant, nicht als globales Plattform-Limit. |
| 5 | Kein Cross-Tenant-Zugriff | Ein Modul kann niemals Daten eines anderen Tenants lesen oder schreiben. Der SDK-Prisma-Client erzwingt tenantId auf Typen-Ebene. |
| 6 | Öffentliche URLs | Öffentlich zugängliche Modul-Seiten laufen unter der Subdomain des Tenants: {subdomain}.prilog.team/{modul-pfad}. |
| 7 | Daten bei Kündigung | Bei Deaktivierung: Daten bleiben 30 Tage erhalten. Danach sicheres Löschen. Keine Übertragung auf andere Tenants. |
| 8 | Zustandslosigkeit | Module dürfen keinen globalen In-Memory-State haben der zwischen Tenants geteilt wird. PM2-Cluster und Redeployments dürfen keinen Datenverlust verursachen. |
11. Performance-Vertrag
Jedes Modul schließt mit dem Core einen impliziten Performance-Vertrag. Ein Modul das diesen Vertrag verletzt wird aus dem Store entfernt und für betroffene Tenants automatisch deaktiviert.
11.1 Verbindliche Limits
| Metrik | Limit | Konsequenz bei Überschreitung |
|---|---|---|
| API-Response-Zeit (p95) | < 200ms | Warnung nach 3 aufeinanderfolgenden Überschreitungen. Deaktivierung nach 10. |
| API-Response-Zeit (p99) | < 500ms | Warnung nach erster Überschreitung. Untersuchungspflicht. |
| DB-Query-Zeit (p95) | < 50ms | Hinweis auf fehlende Indizes. Pflicht: Index-Review innerhalb 7 Tagen. |
| Event-Handler Dauer | < 100ms | Lange Handler blockieren den Event-Bus. Async-Verarbeitung über Job-Queue. |
| Speicherverbrauch | < 128 MB pro Tenant | Throttling. Bei dauerhafter Überschreitung: Deaktivierung. |
| HTTP-Fehlerrate | < 1% | Automatische Warnung an Entwickler und Prilog-Team. |
11.2 Pflicht-Patterns
Caching
Häufig abgerufene, selten veränderte Daten müssen gecacht werden. Der SDK stellt einen tenant-isolierten Redis-Cache bereit:
const feed = await ctx.cache.get("ical-feed");
if (!feed) {
const data = await generateFeed(ctx);
await ctx.cache.set("ical-feed", data, { ttl: 300 }); // 5 Minuten
}Datenbankindizes
Jede Spalte die in einer WHERE-Bedingung oder ORDER-BY-Klausel vorkommt braucht einen Index:
model CalendarEvent {
id String @id @default(cuid())
tenantId String
layerId String
dtstart DateTime
dtend DateTime
@@index([tenantId, layerId, dtstart, dtend])
@@index([tenantId])
}Job-Queue für schwere Operationen
Alles was länger als 100ms dauern kann muss asynchron in einer Job-Queue verarbeitet werden:
// Falsch: Sync-Job blockiert den Request
// await syncCalDAV(tenantId); ← niemals so
// Korrekt: Job in Queue legen, sofort antworten
await ctx.queue.add("caldav-sync", { tenantId, syncId });
return { status: "queued" };12. Der Prilog Store
Der Prilog Store ist der Marktplatz für alle Module. Er ist der zentrale Ort wo Schulen Module entdecken, testen und buchen — und wo Entwickler ihre Arbeit veröffentlichen und monetarisieren.
12.1 Store-Kategorien
| Kategorie | Beschreibung |
|---|---|
| Kommunikation | Erweiterungen für Chat, Benachrichtigungen, Integrationen mit externen Kommunikationsdiensten. |
| Organisation | Kalender, Aufgabenverwaltung, Stundenpläne, Raumverwaltung. |
| Analytics | Nutzungsauswertungen, Engagement-Metriken, Compliance-Reports. |
| Integration | Anbindung externer Systeme: SIS, LDAP, Videokonferenz, LMS. |
| Sicherheit | Zwei-Faktor-Authentifizierung, Audit-Logs, DSGVO-Werkzeuge. |
| Sonstiges | Alle Module die keiner anderen Kategorie zuzuordnen sind. |
12.2 Veröffentlichungs-Prozess
| # | Schritt | Beschreibung | Dauer |
|---|---|---|---|
| 1 | Entwickler-Account | Registrierung als Entwickler unter store.prilog.chat. Identitätsverifikation. | 1–3 Werktage |
| 2 | Modul einreichen | Upload des kompilierten Dist-Ordners + prilog-module.json. Automatische Manifest-Validierung und Dependency-Check. | Sofort |
| 3 | Sicherheits-Scan | Automatisierter Scan auf bekannte Sicherheitslücken, verdächtige Netzwerkkommunikation, undeklarierte Berechtigungen. | < 10 Minuten |
| 4 | Kompatibilitäts-Test | Modul wird gegen aktuelle Core-Version in Test-Umgebung ausgeführt. | < 30 Minuten |
| 5 | Manuelle Review | Für Typ B und C: Code-Review durch Prilog-Team. Fokus auf Datenschutz, Datenisolation, Performance-Patterns. | 1–5 Werktage |
| 6 | Freigabe | Modul erscheint im Store. Entwickler wird per E-Mail benachrichtigt. | Sofort |
| 7 | Updates | Neue Versionen durchlaufen denselben Prozess. Kritische Sicherheitsupdates: beschleunigter Review-Track (< 24h). | Wie oben |
12.3 Store-Seite eines Moduls
Jedes Modul hat eine Store-Seite mit folgenden Elementen:
- Name, Icon, Kurzbeschreibung, Kategorie, Modul-Typ
- Ausführliche Beschreibung (aus README.md, Markdown gerendert)
- Screenshots und Demo-Video (optional)
- Abrechnungsmodell und Preis
- Kompatible Core-Versionen
- Installationszahl und Bewertungen (1–5 Sterne)
- Entwickler-Profil und Support-Link
- Versions-Historie (CHANGELOG.md)
- Datenschutz-Information: welche Daten verarbeitet das Modul
13. Abrechnungs-Modelle
Prilog unterstützt drei Abrechnungsmodelle für Module. Das gewählte Modell wird im Manifest deklariert und ist für alle Versionen einer Major-Version verbindlich. Ein Wechsel des Modells erfordert eine neue Major-Version.
13.1 Subscription (Abonnement)
Das Modul wird monatlich pro Tenant abgerechnet. Der Preis ist fest, unabhängig von der tatsächlichen Nutzung.
| Aspekt | Details |
|---|---|
| Anwendungsfall | Module mit kontinuierlichem Mehrwert: Kalender, Projekt-Modul, erweiterte Analytik. |
| Manifest | "model": "subscription", "monthlyPrice": 4.90, "currency": "EUR" |
| Testphase | "trialDays": 30 — Tenant kann das Modul 30 Tage kostenlos testen. |
| Billing-Hook | ctx.billing.reportActive() — einmal pro Abrechnungszeitraum aufrufen. |
| Kündigung | Zum Monatsende. Laufender Monat wird nicht erstattet. Daten bleiben 30 Tage erhalten. |
13.2 Pay-per-Use (Nutzungsbasiert)
| Aspekt | Details |
|---|---|
| Anwendungsfall | Module mit klar messbaren Einheiten: SMS-Versand, externe API-Calls, exportierte Dateien. |
| Manifest | "model": "pay-per-use", "unitPrice": 0.05, "unit": "sms", "currency": "EUR" |
| Billing-Hook | ctx.billing.reportUsage({ type: "sms", quantity: 1 }) — bei jedem Nutzungsereignis. |
| Kostendeckel | "maxMonthlyPrice": 49.90 — optionaler monatlicher Deckel pro Tenant. |
13.3 One-Time (Einmalkauf)
| Aspekt | Details |
|---|---|
| Anwendungsfall | Module ohne laufende Betriebskosten: Branding-Anpassungen, Report-Templates, Einmal-Importer. |
| Manifest | "model": "one-time", "price": 29.00, "currency": "EUR" |
| Updates | Minor- und Patch-Updates sind kostenlos. Major-Updates können als neue Kaufversion angeboten werden. |
13.4 Kostenlos
Module ohne billing-Eintrag im Manifest sind kostenlos und können ohne Preisbestätigung installiert werden.
13.5 Umsatzbeteiligung
Externe Entwickler erhalten 70% des Netto-Verkaufspreises. Prilog behält 30% als Plattformgebühr. Die Auszahlung erfolgt monatlich per Banküberweisung, ab einem Mindestauszahlungsbetrag von 50 EUR.
14. Sicherheit & Datenschutz
14.1 Sicherheits-Grundsätze
- Module dürfen Nutzerpasswörter und Auth-Token weder lesen noch speichern.
- Module dürfen keine externen Skripte oder Stylesheets in das Prilog-Frontend nachladen.
- Module dürfen keine Daten an externe Dienste senden ohne explizite Berechtigung (
external:http) und Whitelist. - Module dürfen keinen Server-Side-Code außerhalb des eigenen
src/-Verzeichnisses ausführen. - Module dürfen keine Umgebungsvariablen des Core-Servers lesen oder verändern.
14.2 DSGVO-Anforderungen
Alle Module die personenbezogene Daten verarbeiten müssen im Manifest und auf der Store-Seite folgendes deklarieren:
- Welche Kategorien personenbezogener Daten verarbeitet werden
- Ob Daten an externe Dienste übertragen werden (und welche)
- Wie lange Daten gespeichert werden
- Ob eine Auftragsverarbeitungsvereinbarung (AVV) erforderlich ist
DSGVO-Pflicht
Alle Modul-Daten liegen auf dem Prilog-Schulserver in Deutschland. Module mit external:http-Berechtigung müssen pro externem Dienst eine Datenschutzerklärung beifügen. Module die gegen diese Anforderungen verstoßen werden ohne Vorankündigung aus dem Store entfernt.
14.3 Verantwortlichkeiten
| Partei | Verantwortung |
|---|---|
| Prilog | Stellt sichere Core-Infrastruktur, SDK, Isolation, Billing und Store-Infrastruktur bereit. Prüft Module vor Veröffentlichung. Haftet nicht für Schäden durch externe Module. |
| Entwickler | Haftet für korrekte Funktion, Datenschutzkonformität und Sicherheit des eigenen Moduls. Verpflichtet sich zur schnellen Behebung kritischer Sicherheitslücken (< 48h). |
| Tenant-Admin | Entscheidet welche Module für seine Schule aktiviert werden. Bestätigt Berechtigungen bei der Installation. Trägt Verantwortung für den pädagogischen Einsatz. |
15. SDK & Entwickler-Quickstart
15.1 SDK installieren
# Neues Modul aus Template erstellen
npx @prilog/create-module mein-modul
# Oder manuell installieren
npm install @prilog/sdk @prilog/types
npm install -D @prilog/dev-server @prilog/module-validator15.2 Lokale Entwicklungsumgebung
- Repository klonen:
git clone https://github.com/prilog/prilog-dev-sandbox - Dev-Server starten:
npm run dev— startet Prilog Core + lokale Store-Registry - Modul-Verzeichnis einhängen:
npx prilog-dev link ./mein-modul - Modul im lokalen Admin aktivieren und testen
- Logs und Event-Bus-Traffic im Dev-Dashboard beobachten
15.3 Wichtige SDK-Typen
import type {
PrilogModuleContext, // Haupt-Kontext: db, cache, events, billing, auth
AuthContext, // tenant, userId, systemRole, spaceIds
ModuleRouteHandler, // Typisierung für Route-Handler
CoreEvent, // Union-Typ aller Core-Events
BillingUsageEvent, // Payload für ctx.billing.reportUsage()
} from "@prilog/sdk";16. Checkliste – Modul ist bereit
Bevor ein Modul im Store eingereicht wird, muss diese Checkliste vollständig abgehakt sein.
Manifest & Dokumentation
- [ ]
prilog-module.jsonvorhanden und vollständig ausgefüllt - [ ] Alle angeforderten Berechtigungen sind tatsächlich notwendig (keine Überprivilegierung)
- [ ]
README.mdvorhanden, verständlich, mit Installationsanleitung und Screenshots - [ ]
CHANGELOG.mdvorhanden (ab v1.0)
Tenant-Fähigkeit
- [ ] Jede DB-Tabelle enthält
tenantId - [ ] Jede Query filtert auf
tenantId - [ ] Kein globaler In-Memory-State
- [ ] Öffentliche URLs enthalten die Tenant-Subdomain
- [ ] Daten-Löschung bei Deaktivierung implementiert
Performance
- [ ] DB-Indizes auf alle WHERE- und ORDER-BY-Spalten gesetzt
- [ ] Häufig abgerufene Daten sind gecacht (
ctx.cache) - [ ] Alle Operationen > 100ms laufen in der Job-Queue
- [ ] Keine n+1-Query-Probleme (Prisma
includestatt separate Queries) - [ ] Modul startet und antwortet innerhalb von 200ms nach Aktivierung
Sicherheit
- [ ] Keine Passwörter oder Tokens werden gespeichert
- [ ]
external:http: Whitelist aller Domains im Manifest deklariert - [ ] Input-Validierung auf allen Route-Parametern (zod oder ähnliches)
- [ ] SQL-Injection nicht möglich (nur Prisma-Client, keine Raw-Queries)
- [ ] Keine sensitiven Daten in Log-Ausgaben
Tests
- [ ] Unit-Tests für alle Services vorhanden
- [ ] Integrationstests für alle API-Routen vorhanden
- [ ] Mindestabdeckung: 70% (Pflicht), 85% (empfohlen)
- [ ] Tests laufen erfolgreich gegen aktuelle Core-Version durch
Billing (falls zutreffend)
- [ ] Abrechnungsmodell im Manifest korrekt konfiguriert
- [ ]
ctx.billing.reportActive()oderctx.billing.reportUsage()korrekt implementiert - [ ] Verhalten bei Kündigung und Daten-Ablauf getestet