Skip to content

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

StatusZielgruppeReferenz
ImplementiertEntwickler & Architektenprilog.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-validatorprilog-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

BegriffDefinition
CoreDas 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.
ModulEine eigenständige, abgegrenzte Funktionalitätserweiterung. Lebt vollständig außerhalb des Core. Kommuniziert nur über offizielle Schnittstellen. Kann aktiviert, deaktiviert, gemietet und bewertet werden.
TenantEine 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 StoreDer Marktplatz für Prilog-Module. Ähnlich dem WordPress Plugin Directory. Schulen entdecken, buchen und verwalten Module. Entwickler veröffentlichen und monetarisieren ihre Module.
Modul-SDKDas 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-BusDas 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-FlagEin 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.
AuthContextDas 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.
ManifestEine 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

SchnittstelleBeschreibung
Platform-APIVersionierte REST-Endpunkte (/api/platform/v1/*). Authentifizierung, Nutzer- und Space-Abfragen, Feature-Flags, Bootstrap. Einziger erlaubter Zugangspunkt für Modul-Backend-Code zum Core.
Event-BusInternes Pub/Sub-System. Module subscriben auf Core-Events und publizieren eigene Events. Keine direkte Kopplung zwischen Produzent und Konsument.
AuthContextWird bei jedem Request automatisch aufgelöst und dem Modul übergeben. Enthält tenant, userId, roles. Module müssen Auth nie selbst implementieren.
Modul-RegistryDie zentrale Datenbank-Tabelle module_registrations. Hier sind alle bekannten Module mit Manifest, Version und Status eingetragen.
Billing-HookEin standardisierter Callback-Punkt. Module melden Nutzungsereignisse (Events, aktivierte Tenants, Dateivolumen) an den Core. Der Core leitet es an die Billing-API weiter.
Store-APIDie 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.

TypBeschreibungErlaubte SchnittstellenReview-Aufwand
Typ A – UI-ErweiterungFügt neue Ansichten oder Widgets im Frontend hinzu. Keine eigene Backend-Logik, keine eigene DB.Platform-API (read-only), Feature-FlagsGering
Typ B – Backend-ModulEigene API-Routen, eigene DB-Tabellen, eigene Business-Logik. Kein Zugriff auf Core-Tabellen.Platform-API, Event-Bus, eigene DB-Tabellen, Billing-HookMittel
Typ C – IntegrationVerbindet Prilog mit externen Diensten (z.B. Schulverwaltungssysteme, Kalender-Apps, LDAP).Platform-API, Event-Bus, externe APIs, Billing-HookHoch
Typ D – Core-nahe ErweiterungErweitert die Matrix/Synapse-Schicht (Application Services). Nur für Prilog-interne Entwicklung.Alle Schnittstellen + Synapse Admin APISehr 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

FeldPflichtBeschreibung
idJaEindeutiger Bezeichner. Format: prilog-{name}. Nur Kleinbuchstaben, Bindestriche. Unveränderlich nach Veröffentlichung.
nameJaAnzeigename im Store. Max. 60 Zeichen.
versionJaSemantic Versioning: MAJOR.MINOR.PATCH
typeJaA | B | C | D — Modul-Typ (siehe Kapitel 4)
prilogCoreVersionJaMinimale Core-Version. Format: >=1.2.0. Modul wird nicht installiert wenn Core älter ist.
descriptionJaKurzbeschreibung (max. 200 Zeichen). Erscheint im Store.
longDescriptionNeinAusführliche Beschreibung mit Markdown. Erscheint auf der Store-Detailseite.
authorJaName und E-Mail des Entwicklers oder der Organisation.
licenseJaSPDX-Lizenzkennung oder "proprietary".
permissionsJaArray der angeforderten Berechtigungen (siehe Kapitel 6).
billingNeinAbrechnungs-Konfiguration (siehe Kapitel 11). Fehlt = kostenlos.
migrationsNeinPfad zu DB-Migrations-Dateien. Nur Typ B und C.
routesNeinPfad zur Route-Registrierungsdatei. Nur Typ B und C.
eventSubscriptionsNeinListe der Core-Events auf die das Modul hört.
featureFlagJaName des Feature-Flags im Bootstrap-Response. z.B. "calendar"
storeCategoryNeinKategorie im Store: communication | organization | analytics | integration | other
screenshotsNeinArray von Screenshot-URLs für die Store-Seite.
supportUrlNeinURL zum Support-Kanal des Entwicklers.
documentationUrlNeinURL zur Modul-Dokumentation.

5.2 Beispiel-Manifest

json
{
  "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

BerechtigungZugriff aufTyp
users:readNutzerliste des Tenants lesen (ohne Passwörter, ohne private Daten)Lesend
spaces:readSpace- und Raum-Struktur des Tenants lesenLesend
spaces:writeSpaces und Räume erstellen und bearbeitenSchreibend
messages:readChat-Nachrichten lesen (nur in freigegebenen Räumen)Lesend
events:publishEvents auf dem Event-Bus veröffentlichenSchreibend
events:subscribeAuf Core-Events hörenLesend
files:readDateien aus dem Prilog-Dateispeicher lesenLesend
files:writeDateien in den Prilog-Dateispeicher schreibenSchreibend
billing:reportNutzungsereignisse an die Billing-API meldenSystem
notifications:sendBenachrichtigungen an Nutzer senden (über Core-Benachrichtigungsdienst)Schreibend
external:httpHTTP-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:http erfordert 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

PfadInhalt & Zweck
prilog-module.jsonPflicht. Das Manifest (siehe Kapitel 5).
README.mdPflicht. Entwickler-Dokumentation. Wird auf der Store-Seite angezeigt.
CHANGELOG.mdPflicht ab v1.0. Versionshistorie mit Breaking Changes.
src/Gesamter Quellcode des Moduls.
src/index.tsPflicht. 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.tsTypeScript-Typen und Interfaces. Exportiert für SDK-Konsumenten.
prisma/schema.module.prismaDB-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.

typescript
// 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.

#StatusWas passiertRückgängig?
1EntwicklungEntwickler baut Modul lokal gegen Prilog-Dev-Server. SDK stellt Test-Fixtures für Core-Events bereit.n/a
2ReviewModul wird bei Prilog eingereicht. Automatische Sicherheits- und Kompatibilitätsprüfung. Manuelle Code-Review für Typ C und D.Immer
3Im StoreModul ist im Prilog Store verfügbar. Noch nicht bei Tenants aktiv.Jederzeit zurückziehen
4InstalliertTenant-Admin installiert Modul. Manifest wird validiert. Berechtigungen werden angezeigt und bestätigt.Deinstallieren
5MigrationsDB-Migrations werden für diesen Tenant ausgeführt. Bei Fehler: automatischer Rollback, Modul bleibt inaktiv.Automatischer Rollback
6AktivRouten werden registriert. Feature-Flag gesetzt. Event-Subscriptions aktiv. Abrechnung startet.Deaktivieren
7DeaktiviertRouten werden deregistriert. Feature-Flag gelöscht. Events werden nicht mehr verarbeitet. Abrechnung stoppt.Reaktivieren
8Daten-Frist30-Tage-Frist nach Deaktivierung. Modul-Daten bleiben erhalten. Reaktivierung stellt vollen Zustand wieder her.Reaktivieren innerhalb 30 Tage
9GelöschtModul-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      │
└─────────────────────┘     └─────────────────────┘     └─────────────────────┘
StufeWerWasWoTechnisch
1Prilog-AdminModul in der Registry freigebenAdmin-Panel → Modul-RegistryPATCH /admin/module-registry/:moduleId setzt ModuleRegistration.status auf active
2Kunden-Admin (Schule)Modul installieren und aktivierenPortal → Modul-StorePOST /modules/install + POST /modules/installed/:id/activate erzeugt TenantModuleInstallation
3EndnutzerModul nutzenWeb-ClientGET /bootstrap liefert Feature-Flags, Frontend zeigt nur aktive Module

Stufe 1 — Admin-Panel (Prilog-intern):

  • Der Prilog-Admin sieht alle Module in der ModuleRegistration Tabelle
  • Er kann den Status auf active, inactive, deprecated oder removed setzen
  • Nur Module mit Status active erscheinen 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 → TenantModuleInstallation mit Status installed
  • 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 TenantModuleInstallation mit Status active
  • 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.

EndpunktBeschreibung
GET /api/platform/v1/bootstrapFeature-Flags, Modul-Konfiguration, Nutzerrolle für diesen Tenant. Wird beim App-Start vom Frontend abgerufen.
GET /api/platform/v1/usersNutzerliste des Tenants. Erfordert Berechtigung users:read.
GET /api/platform/v1/spacesSpace- und Raum-Struktur. Erfordert spaces:read.
GET /api/platform/v1/meProfil des aktuellen Nutzers. Immer verfügbar.
POST /api/platform/v1/notifyBenachrichtigung 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)

EventBeschreibung
user.createdEin neuer Nutzer wurde angelegt. Payload: { userId, tenantId, role }
user.joinedNutzer ist einem Space beigetreten. Payload: { userId, spaceId, tenantId }
user.leftNutzer hat einen Space verlassen.
space.createdEin neuer Space wurde erstellt. Payload: { spaceId, tenantId, name }
space.deletedEin Space wurde gelöscht.
room.createdEin neuer Raum wurde in einem Space erstellt.
module.activatedEin Modul wurde für einen Tenant aktiviert. Payload: { moduleId, tenantId }
module.deactivatedEin Modul wurde für einen Tenant deaktiviert.
billing.period.endAbrechnungszeitraum 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.

typescript
// 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 undefined

9.4 Billing-Hook

Module mit Abrechnungsmodell melden Nutzungsereignisse über den standardisierten Billing-Hook an den Core.

MethodeBeschreibung
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.

#RegelVerbindliche Anforderung
1DatenisolationJede 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.
2Aktivierung pro TenantDas Modul kann für Tenant A aktiv und für Tenant B inaktiv sein. Status, Konfiguration und Daten sind immer pro Tenant getrennt.
3Konfiguration pro TenantModul-Einstellungen werden in module_config(tenantId, moduleId, key, value) gespeichert. Kein globaler Konfigurations-State.
4Rate-Limits pro TenantAPI-Limits und Ressourcengrenzen gelten pro Tenant, nicht als globales Plattform-Limit.
5Kein Cross-Tenant-ZugriffEin 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}.
7Daten bei KündigungBei Deaktivierung: Daten bleiben 30 Tage erhalten. Danach sicheres Löschen. Keine Übertragung auf andere Tenants.
8ZustandslosigkeitModule 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

MetrikLimitKonsequenz bei Überschreitung
API-Response-Zeit (p95)< 200msWarnung nach 3 aufeinanderfolgenden Überschreitungen. Deaktivierung nach 10.
API-Response-Zeit (p99)< 500msWarnung nach erster Überschreitung. Untersuchungspflicht.
DB-Query-Zeit (p95)< 50msHinweis auf fehlende Indizes. Pflicht: Index-Review innerhalb 7 Tagen.
Event-Handler Dauer< 100msLange Handler blockieren den Event-Bus. Async-Verarbeitung über Job-Queue.
Speicherverbrauch< 128 MB pro TenantThrottling. 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:

typescript
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:

prisma
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:

typescript
// 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

KategorieBeschreibung
KommunikationErweiterungen für Chat, Benachrichtigungen, Integrationen mit externen Kommunikationsdiensten.
OrganisationKalender, Aufgabenverwaltung, Stundenpläne, Raumverwaltung.
AnalyticsNutzungsauswertungen, Engagement-Metriken, Compliance-Reports.
IntegrationAnbindung externer Systeme: SIS, LDAP, Videokonferenz, LMS.
SicherheitZwei-Faktor-Authentifizierung, Audit-Logs, DSGVO-Werkzeuge.
SonstigesAlle Module die keiner anderen Kategorie zuzuordnen sind.

12.2 Veröffentlichungs-Prozess

#SchrittBeschreibungDauer
1Entwickler-AccountRegistrierung als Entwickler unter store.prilog.chat. Identitätsverifikation.1–3 Werktage
2Modul einreichenUpload des kompilierten Dist-Ordners + prilog-module.json. Automatische Manifest-Validierung und Dependency-Check.Sofort
3Sicherheits-ScanAutomatisierter Scan auf bekannte Sicherheitslücken, verdächtige Netzwerkkommunikation, undeklarierte Berechtigungen.< 10 Minuten
4Kompatibilitäts-TestModul wird gegen aktuelle Core-Version in Test-Umgebung ausgeführt.< 30 Minuten
5Manuelle ReviewFür Typ B und C: Code-Review durch Prilog-Team. Fokus auf Datenschutz, Datenisolation, Performance-Patterns.1–5 Werktage
6FreigabeModul erscheint im Store. Entwickler wird per E-Mail benachrichtigt.Sofort
7UpdatesNeue 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.

AspektDetails
AnwendungsfallModule 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-Hookctx.billing.reportActive() — einmal pro Abrechnungszeitraum aufrufen.
KündigungZum Monatsende. Laufender Monat wird nicht erstattet. Daten bleiben 30 Tage erhalten.

13.2 Pay-per-Use (Nutzungsbasiert)

AspektDetails
AnwendungsfallModule mit klar messbaren Einheiten: SMS-Versand, externe API-Calls, exportierte Dateien.
Manifest"model": "pay-per-use", "unitPrice": 0.05, "unit": "sms", "currency": "EUR"
Billing-Hookctx.billing.reportUsage({ type: "sms", quantity: 1 }) — bei jedem Nutzungsereignis.
Kostendeckel"maxMonthlyPrice": 49.90 — optionaler monatlicher Deckel pro Tenant.

13.3 One-Time (Einmalkauf)

AspektDetails
AnwendungsfallModule ohne laufende Betriebskosten: Branding-Anpassungen, Report-Templates, Einmal-Importer.
Manifest"model": "one-time", "price": 29.00, "currency": "EUR"
UpdatesMinor- 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

ParteiVerantwortung
PrilogStellt 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.
EntwicklerHaftet für korrekte Funktion, Datenschutzkonformität und Sicherheit des eigenen Moduls. Verpflichtet sich zur schnellen Behebung kritischer Sicherheitslücken (< 48h).
Tenant-AdminEntscheidet 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

bash
# 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-validator

15.2 Lokale Entwicklungsumgebung

  1. Repository klonen: git clone https://github.com/prilog/prilog-dev-sandbox
  2. Dev-Server starten: npm run dev — startet Prilog Core + lokale Store-Registry
  3. Modul-Verzeichnis einhängen: npx prilog-dev link ./mein-modul
  4. Modul im lokalen Admin aktivieren und testen
  5. Logs und Event-Bus-Traffic im Dev-Dashboard beobachten

15.3 Wichtige SDK-Typen

typescript
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.json vorhanden und vollständig ausgefüllt
  • [ ] Alle angeforderten Berechtigungen sind tatsächlich notwendig (keine Überprivilegierung)
  • [ ] README.md vorhanden, verständlich, mit Installationsanleitung und Screenshots
  • [ ] CHANGELOG.md vorhanden (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 include statt 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() oder ctx.billing.reportUsage() korrekt implementiert
  • [ ] Verhalten bei Kündigung und Daten-Ablauf getestet