CodamAIDocs
Themafertig

Eine Spur für alles

Ein einziger Zuhörer schreibt jedes fachliche Ereignis in eine Tabelle, die nur wächst. Wie ein Eintrag aussieht.

Ausprägungen
Ereignis mit angemeldetem AufruferEreignis ohne Aufruferlanger Text wird gekürztSchreiben scheitertÄnderung wird zurückgerollt

Worum es geht

Ein Audit ist ein Protokoll, das festhält, wer was wann getan hat. In CIAS betrifft das Personen und Rechte: Ein Mandant wurde angelegt, eine Person gesperrt, eine Rolle vergeben. Ein Prüfer will später genau das nachlesen können, an einer Stelle und in einer Reihenfolge.

Das Bild

flowchart LR
    T["cias-tenancy<br/>TenantEvent"] --> L
    U["cias-user<br/>UserEvent"] --> L
    A["cias-authorization<br/>AuthorizationEvent"] --> L
    R["cias-registration<br/>RegistrationEvent"] --> L
    L["Zuhörer<br/>DomainEventAuditListener"] -- "+ wer handelt,<br/>aus dem Token" --> DB[("cias_audit_entry<br/>System-Datenbank")]

Lies es so: Jedes Modul meldet seine Ereignisse, ohne zu wissen, dass jemand zuhört. Der Zuhörer hört auf jedes fachliche Ereignis, nicht auf eine Liste ausgewählter. Meldet ein Modul morgen eine neue Art von Ereignis, landet sie ohne weiteres Zutun im Audit.

Warum eine Spur und nicht vier

  • Ein Prüfer liest eine Geschichte. Vier Tabellen, die man über Zeitstempel zusammensetzen müsste, wären eine Rekonstruktion. Genau die soll ein Audit überflüssig machen.
  • Ein Modul protokolliert sich nicht selbst. Schriebe jedes Modul seine Einträge selbst, schriebe der Code, dessen Verhalten geprüft wird, auch das Protokoll darüber.
  • Die Spur liegt in der System-Datenbank, nicht in einer Mandanten-Datenbank. Manche Ereignisse betreffen Mandanten, die noch gar keine eigene Datenbank haben, und wer das Audit liest, liest über alle Mandanten hinweg.

Wie ein Eintrag aussieht

FeldInhaltBeispiel
iddie ID des Eintrags9b1e…
typedie Art des Ereignisses, als Gruppe.ArtTenantEvent.Suspended
detaildas Ereignis, wie es sich selbst beschreibt: alle Felder mit WertenSuspended[tenantId=4f2a…, tenantKey=nordbau, occurredOn=2026-09-22T09:14:03Z]
actorIdwer gehandelt hat: die ID des Kontos in Keycloak (sub aus dem Token)5c9e…
actorTenantder Mandant, unter dem die Anfrage lief, falls es einen gabnordbau
occurredOnwann es fachlich passiert ist2026-09-22T09:14:03Z
recordedOnwann der Eintrag geschrieben wurde2026-09-22T09:14:03.120Z

Was passiert ist, kommt aus dem Ereignis. Wer gehandelt hat, liest der Zuhörer aus dem Token der laufenden Anfrage. Die beiden Zeitpunkte liegen normalerweise Millisekunden auseinander. Sie unterscheiden sich genau dann, wenn es wichtig ist.

Wann der Eintrag geschrieben wird

Erst nachdem die Änderung festgeschrieben ist. Ein Modul meldet sein Ereignis noch während es die Änderung schreibt. Der Zuhörer wartet aber, bis diese Transaktion abgeschlossen ist, und schreibt den Eintrag dann in einer eigenen Transaktion.

Daraus folgen zwei Regeln:

  • Das Audit entscheidet nie, ob eine Änderung passiert. Kann der Eintrag nicht geschrieben werden, bleibt die Änderung trotzdem bestehen. Es entsteht eine Lücke, und das Log zeigt sie.
  • Es gibt keinen Eintrag für etwas, das nicht passiert ist. Wird eine Änderung zurückgerollt, schreibt der Zuhörer nichts.

Läuft gerade keine Transaktion, etwa bei einem Zeitgeber, schreibt der Zuhörer sofort.

Die Ausprägungen

Wie ein Eintrag entsteht

Wann: Ein Plattform-Administrator sperrt einen Mandanten über die Verwaltungs-API.

Der Zuhörer liest sub und Mandanten aus dem Token und trägt sie als actorId und actorTenant ein.

Ergebnis: Eintrag mit Handelndem

Wann: Ein Zeitgeber lässt eine befristete Rolle ablaufen, der Abgleich läuft beim Start, oder eine Person registriert sich selbst, ohne angemeldet zu sein.

Es gibt kein Token. actorId und actorTenant bleiben leer. Leer heißt: Die Plattform hat selbst gehandelt, oder niemand war angemeldet. CIAS schreibt absichtlich keinen Platzhalter wie system: Der sähe aus wie ein Konto, das so heißt.

Ergebnis: Eintrag ohne Handelnden

Wann: Das Ereignis beschreibt sich mit mehr als 4000 Zeichen.

CIAS kürzt detail und hängt …[cut] an. Ein Ereignis, das passiert ist, soll nicht fehlen, nur weil sein Text lang war. Die Markierung zeigt, dass es nicht die ganze Geschichte ist.

Ergebnis: Eintrag mit gekürztem Text

Wann: Die Datenbank nimmt den Eintrag nicht an.

Die Änderung ist zu diesem Zeitpunkt schon festgeschrieben und bleibt bestehen. CIAS schreibt einen Fehler ins Log, mit der Art des Ereignisses, aber ohne seinen Text. Der Text könnte Kundendaten enthalten, und das Log wird anderswo aufbewahrt.

Ergebnis: Lücke in der Spur, erkennbar im Log

Wann: Das Modul hat sein Ereignis gemeldet, danach scheitert die Änderung und wird zurückgerollt.

Der Zuhörer schreibt nichts. Ein Eintrag würde eine Tat bezeugen, die nie stattgefunden hat.

Ergebnis: kein Eintrag

Nur anhängen

Es gibt keine Funktion, die einen Eintrag ändert oder löscht, weder in Java noch über HTTP. Ein Protokoll, das man nachträglich korrigieren kann, beantwortet eine andere Frage als die, die ein Prüfer stellt.

Die Datenbank selbst erzwingt das nicht. Wer es auch dort erzwingen will, etwa mit einem Trigger oder indem der Anwendung das Recht zum Ändern entzogen wird, entscheidet das für seine eigene Datenbank.

Einschalten

Das Audit gehört zum Modul cias-audit:

EinstellungBedeutung
codamai.cias.audit.enabledtrue: Zuhörer und Lesezugriff sind da
codamai.cias.audit.persistencejpa: Einträge landen in cias_audit_entry

Das eigenständige CIAS schaltet beides ein, das hub-backend mit eingebettetem CIAS ebenso. Dort liegt die Tabelle in der System-Datenbank, und der Eintrag wird wie überall erst nach dem Festschreiben der Änderung geschrieben. Eine andere eingebettete Installation hat das Audit nur, wenn sie das Modul einbindet und einschaltet.

Fallen

Weiter

Quellen im Code und in der Wissensdatenbank
  • CIAS/cias-audit – DomainEventAuditListener (on, entryFor, typeOf, caller), AuditEntry (ofSystem, ofCaller, MAX_DETAIL, CUT), AuditTrail, JpaAuditTrailAdapter.append (REQUIRES_NEW), @TransactionalEventListener AFTER_COMMIT
  • CIAS/cias-audit – db/migration/cias-audit/V1__cias_audit_entry.sql, CiasAuditConfiguration (codamai.cias.audit.enabled, persistence)
  • CIAS/cias-kernel – DomainEvent (occurredOn), CallerContext (subject, tenantKey)
  • CIAS/cias-runtime – application.yml (codamai.cias.audit)
  • CIAS/cias-audit/docs/adr – ADR-033
Suchen