Zum Hauptinhalt springen

Audit-Log

Das Audit-Log erfasst jede administrative Aktion gegen deinen Mandanten: wer was wann von welcher IP aus an welcher Ressource getan hat. Die Aufbewahrung hat zwei Stufen: eine abfragbare heiße Stufe mit vollständigem Ereignisinhalt (Akteur, Aktion, fields_json) für 90 / 180 / 365 / 730 Tage, standardmäßig 90 (Standardplan) oder 365 (Enterprise-Pläne) und pro Mandant einstellbar unter Authentifizierungsrichtlinie → Aufbewahrung des Audit-Logs; und ein kryptografisches Kettenarchiv für 3 Jahre in T Cloud Public OBS WORM, das die Ankerobjekte der Hash-Kette aufbewahrt, nachdem der Inhalt der heißen Stufe geschwärzt wurde. Compliance-Exporte decken das abfragbare Fenster ab.

Zur kryptografischen Beweiskette (Ankerkette, in OBS gespeicherte Merkle-Commitments) siehe Integrität der Audit-Kette.

Was erfasst wird

EreignisklasseBeispiele
IdentitätAnmeldung, SSO-Step-up, MFA-Zurücksetzung, SCIM-Provisionierung, Rollenänderung
RepositoryRepository angelegt, übertragen, archiviert, gelöscht; Branch-Schutz geändert
SecretsSecret angelegt, rotiert, gelöscht (nur der Name, nie der Wert)
WebhooksZiel hinzugefügt, rotiert, entfernt; Zustellung erneut gesendet
RichtliniePush-Schutz-Regel hinzugefügt oder Override angewendet; Malware-Scan-Override; SAST-Verwerfung
AbrechnungPlanwechsel, Änderung der Zahlungsmethode, Änderung der Obergrenze
DatenexportExport eingereiht, heruntergeladen, abgelaufen

Bewusst nicht enthalten: gewöhnliche Lesezugriffe (Klonen, Ansehen, Datei öffnen). Das Audit-Log ist ein Protokoll administrativer Aktionen, kein Zugriffsprotokoll. Die Ausnahme sind Prüfer mit Lesezugriff, deren Lesezugriffe auf deine Einstellungen erfasst werden (siehe Aktivität von Prüfern). Zugriffe auf die Paket- und Container-Registry (jeder Download, jeder Upload und jedes Löschen, mit Akteur und Client-IP) stehen im separaten Zugriffsprotokoll der Registry, das du über die API lesen, an dein SIEM weiterleiten und mit dem Datenexport mitnehmen kannst. Git-Clone und -Fetch werden noch nicht pro Anfrage protokolliert.

Paket- und Release-Ereignisse

Das Veröffentlichen, Taggen, Löschen und Abweisen interner Artefakte steht im Audit-Log, hash-verkettet wie jedes andere Ereignis und in deinem SIEM-Stream. Zwei Sichten schreiben es:

  • Die Versionssicht, aus der Registry selbst: Eine Version ist erschienen oder verschwunden.
  • Die Anfragesicht, aus dem Upload-Proxy vor jedem Schreibzugriff auf die Registry: ein Ereignis pro angenommenem oder abgewiesenem Schreibzugriff, mit dem Job hinter einem Job-Token und, bei Tags, worauf das Tag vorher gezeigt hat.
AktionWann
package.publishedEine Paketversion ist erschienen (ihre erste Datei ist angekommen).
package.deletedEine Paketversion und ihre Dateien sind weg.
package.upload.acceptedEin Schreibzugriff auf die Registry war erfolgreich: eine Datei, ein npm-Publish, eine weitere Datei in einer bestehenden Version, ein per Digest gepushtes Container-Manifest.
package.upload.refusedfremforge hat einen Schreibzugriff abgewiesen: Malware, Scanner nicht verfügbar, zu groß zum Scannen, ein Gate für Container-Layer.
package.delete.acceptedEin Löschen war erfolgreich (eine Datei, eine Version, ein Container-Manifest per Digest).
package.delete.refusedEin Löschen wurde abgewiesen (außer dem eines Registry-Roboters, siehe unten).
package.tag.createdEin Container-Tag oder npm-Dist-Tag, das es nicht gab, benennt jetzt einen Digest.
package.tag.movedEin Container-Tag benennt jetzt einen anderen Digest. previous_digest ist der, den es vorher benannt hat.
package.tag.setEin Tag wurde geschrieben, und fremforge konnte nicht feststellen, was es vorher benannt hat, oder es benannte denselben Digest (unchanged: true). npm-Dist-Tags sind immer set.
package.tag.deletedEin Tag wurde gelöscht; digest ist das, was es benannt hat.
package.immutable.refusedDas Überschreiben einer veröffentlichten Version wurde abgewiesen (unveränderliche Releases); mit beiden sha256-Werten.
registry_robot.delete_refusedEin Registry-Roboter hat versucht zu löschen; Roboter veröffentlichen, löschen aber nie.
package.files.recorded, package.files.removedDie Dateien einer Version mit ihrem sha256 (Organisationen mit Paketrichtlinien).
forgejo.release.*, forgejo.ref.create, forgejo.ref.deleteEin Release wurde veröffentlicht, bearbeitet oder gelöscht (mit ID, Ziel, Draft- und Pre-Release-Flags und Assets); ein Git-Tag oder Branch wurde angelegt (mit seinem Commit) oder gelöscht.

Jedes Paketereignis trägt:

FeldBedeutung
org, package_type, package_name, package_version, fileWas geschrieben wurde. Eine Anfrage benennt, was ihr Pfad benennt; die Versionssicht benennt die Version.
digestsha256:<hex>: der Digest eines Container-Manifests oder der sha256 eines rohen Upload-Bodys (bei jedem Typ außer npm und Cargo, deren Upload-Bodys die Datei umhüllen, der sha256 der Datei selbst).
actor_kindhuman, robot (ein Registry-Roboter), job (ein Job-Token von Forgejo Actions), bot (ein System-Bot von fremforge), system oder anonymous.
actor_login, actor_idWen die Registry authentifiziert hat.
job_repo, job_run_id, job_run_number, job_idBei einem Job-Token: das Repository und der Lauf (/<owner>/<repo>/actions/runs/<job_run_number>).
decision, reasonBei einer Abweisung oder einem Schreibzugriff, den ein Gate im Beobachtungsmodus durchgelassen hat.
access_event_idDie zugehörige Zeile im Zugriffsprotokoll der Registry, die auch die Client-IP enthält.

Ereignisse der Anfragesicht gibt es seit dem 2026-10-08. Eine Promotion zwischen Repositorys ist in fremforge kein Registry-Konzept, daher gibt es kein Promotion-Ereignis.

Paketzugriff

Zugriff pro Paket und geschützte Namen schreiben:

AktionWann
package_access.settings.updatedDer Standard der Organisation hat sich geändert (default_mode von und zu).
package_access.package.updatedDie Zugriffseinstellung eines Pakets wurde angelegt oder geändert (mode, inherit_repo, changed).
package_access.package.removedEinstellung und Berechtigungen eines Pakets wurden entfernt (grants_removed).
package_access.grant.setEine Berechtigung für ein Team oder für Actions wurde hinzugefügt oder geändert (grantee_kind, grantee_name, permission, previous_permission).
package_access.grant.removedEine Berechtigung wurde entfernt.
package_protection.rule.created, .updated, .deletedEine Regel für geschützte Namen hat sich geändert (package_type, name_pattern, push, delete).
package_access.deniedEine Anfrage wurde durch den Zugriff pro Paket oder einen geschützten Namen abgewiesen (access, needed, permission, reason und die Akteursfelder oben). Höchstens einmal pro Stunde für denselben Akteur, dasselbe Paket und denselben Zugriff.
package_access.would_denyDasselbe, während fremforge die Prüfung im Beobachtungsmodus ausgeführt und die Anfrage bedient hat.

Modell-Scan

Der Modell-Scan schreibt:

AktionWann
security_policy.model_scan.updatedDie Modellrichtlinie der Organisation hat sich geändert (changed und diff mit from und to jeder Einstellung).
security_policy.model_scan.dismissedEin Befund wurde für eine Datei verworfen oder seine Verwerfung erneuert (sha256, verdict, reason, expires_at, replaced).
security_policy.model_scan.dismissal_revokedEine Verwerfung wurde widerrufen (sha256).

Ein Pull, den die Modellrichtlinie abweist, ist ein Ereignis registry.pull.blocked mit gate: model, siehe unten.

Pull-Gates für Container

Die Image-Gates der Registry schreiben ihre Entscheidungen ins Audit-Log, sodass sie wie jedes andere Ereignis dein SIEM erreichen:

AktionWann
registry.pull.blockedEin Container-Pull wurde abgewiesen
registry.pull.would_blockEin Pull wurde bedient, den ein Gate abgewiesen hätte: Deine Sperraktion für den Image-Scan ist warn, oder das Gate läuft im Beobachtungsmodus

Der Akteur ist system:registry-pull-gate. Die fields sind:

FeldBedeutung
gateimage_scan (Schwachstellenbefunde auf oder über deiner Sperrschwelle), image_malware (bösartige Pakete), registry_scan (ein Layer des Images, den ClamAV als infiziert erkannt hat oder nicht scannen konnte), registry_blob (dasselbe für einen direkt per Digest abgerufenen Layer), signature (Image-Signaturen: reason ist unsigned, untrusted oder invalid) oder model (Modell-Scan: reason ist model-malicious, model-suspicious oder model-unscannable)
image, ref, digestDas Image, das angefragte Tag oder der angefragte Digest und der ausgelieferte Digest
reasonWas das Gate gefunden hat, zum Beispiel layer-infected oder image-malware
modeenforce, warn (deine Sperraktion) oder shadow (Beobachtungsmodus)
block_severity, finding_countNur image_scan: die Schwelle und die Zahl der offenen Befunde auf oder über ihr
window_startDie Stunde, für die dieses Ereignis steht
previous_window_start, previous_window_countDie letzte frühere Stunde mit einem Ereignis für denselben Schlüssel und wie viele Entscheidungen sie insgesamt hatte; null beim ersten

Ein Ereignis pro Stunde. Ein CI-Job, der ein blockiertes Image in einer Schleife abruft, würde sonst das Log füllen. Deshalb gibt es pro Image-Digest, Gate und Entscheidung ein Ereignis pro UTC-Stunde, geschrieben bei der ersten Entscheidung der Stunde. Weitere Entscheidungen in dieser Stunde werden gezählt, und die Zahl wird beim nächsten Ereignis für denselben Schlüssel als previous_window_count gemeldet. Die Zahl einer Stunde erreicht dein SIEM also mit dem nächsten Ereignis für diesen Digest, nicht am Ende der Stunde; die Zahl der letzten Stunde mit Pulls wird nicht gesendet. Ein blocked und ein would_block für denselben Digest, oder zwei Gates, sind getrennte Schlüssel.

Ein Ereignis registry.pull.blocked löst außerdem deinen Alarm-Webhook security.pull.blocked aus, falls du einen abonniert hast. Jeder Pull, erlaubt oder nicht, steht im Zugriffsprotokoll der Registry.

Aktivität von Prüfern

Mitglieder des Teams Auditors deiner Organisation können die Einstellungen deiner Organisation lesen, ohne sie zu ändern. Ihre Lesezugriffe werden erfasst, sodass du sehen kannst, was sich ein Prüfer angesehen hat:

AktionWann
tenant.auditor.readEin Prüfer hat eine Admin-Seite geöffnet oder einen lesenden Endpunkt der API aufgerufen. Höchstens ein Ereignis pro Prüfer und Route alle 10 Minuten
tenant.auditor.write_refusedEin Prüfer hat eine Anfrage gesendet, die etwas ändern würde, und sie wurde abgewiesen. Jeder Versuch
tenant.auditor.read_refusedEin Prüfer hat eine Seite oder einen Endpunkt angefragt, der für Prüfer gesperrt ist, etwa den Datenexport, und wurde abgewiesen. Jeder Versuch
compliance.evidence.fetchedJemand, Prüfer oder nicht, hat das signierte Bündel mit Compliance-Nachweisen über die API abgerufen. Jeder Abruf

Bei den drei Ereignissen tenant.auditor.* ist der Akteur der Benutzername des Prüfers, und die fields enthalten die route (das Routenmuster, etwa /:slug/_admin/members, nie den Query-String), die method, bei tenant.auditor.read den status, via (portal, oauth oder token) und bei einem API-Token dessen token_id.

Das Log lesen

Mandanten-Admin → Berichte → Audit-Log. Filtere nach Akteur (<username> oder einem Agenten wie mcp-client:<client id>; der Filter trifft auch die Person, für die ein Agent gehandelt hat), Aktion (secret.created, role.changed, …), Zeitraum oder betroffener Ressource. Die Standardansicht zeigt die letzten 7 Tage.

Klicke auf eine Zeile, um den JSON-Inhalt aufzuklappen. Sensible Felder (Secret-Werte, OIDC-Token, Anhänge mit personenbezogenen Daten) werden schon beim Schreiben geschwärzt, sodass eine aufgeklappte Zeile nie einen Wert zeigt, den der Prüfer eines Kunden nicht lesen sollte.

CSV-Export

Klicke in einer gefilterten Ansicht auf CSV exportieren, um einen Schnappschuss zu erhalten. Die CSV enthält die aktuell gefilterten Zeilen, höchstens 50.000 Zeilen pro Export. Für größere Zeiträume reihst du unter Berichte → Datenexport einen vollständigen Datenexport ein.

Über die REST-API

Das Audit-Log ist über die öffentliche API verfügbar. Nutze sie, wenn du Ereignisse in deine eigene Analyse-Pipeline streamst, ein Compliance-Dashboard baust oder Audit-Abfragen aus einem Skript steuerst.

  • Das Log abfragen GET /_app/api/v1/orgs/:slug/audit (filtern mit ?actor_kind= und ?since=)
  • Integrität der Kette prüfen GET /_app/api/v1/orgs/:slug/audit/integrity
  • Aufbewahrungsfenster ändern PATCH /_app/api/v1/orgs/:slug/auth-policy mit Body { "audit_full_payload_retention_days": 365 }

Erfordert einen Personal Access Token (PAT) mit dem Scope audit:read (oder policy:write, um das Aufbewahrungsfenster zu ändern):

curl -H "Authorization: Bearer ${FREMFORGE_PAT}" \
  "https://frem.sh/_app/api/v1/orgs/acme/audit?actor_kind=human&since=2026-01-01T00:00:00Z"

Die vollständige OpenAPI-Spezifikation findest du in der Referenz der öffentlichen REST-API.

SIEM-Weiterleitung

Jedes Audit-Ereignis wird außerdem an eine HTTPS-Senke pro Mandant gesendet, wenn du die SIEM-Weiterleitung einrichtest. Die Senke erhält Ereignisse in Echtzeit (innerhalb von Sekunden nach der Aktion) in einer kompakten JSON-Form; das Format ist stabil und versioniert.

Aufbewahrung

Audit-Datensätze werden in zwei Stufen aufbewahrt:

  1. Abfragbare Inhaltsstufe: Der Akteursname und der Inhalt fields_json jedes Ereignisses sind während des unten genannten Aufbewahrungsfensters in der Admin-Oberfläche, im CSV-Export und im SIEM-Stream abfragbar.
  2. Manipulationssichere Kettenstufe: Der kryptografische Hash und das Kettenglied jedes Ereignisses bleiben 3 Jahre erhalten (WORM-verankert über OBS Object Lock). Das macht die Kette für die Audit-Bereitschaft nachweislich unverfälscht; sie bleibt intakt, auch nachdem die Schwärzung der Inhaltsstufe eine Zeile markiert hat.

Aufbewahrung des abfragbaren Inhalts

PlanStandardVom Kunden wählbare Voreinstellungen
fremforge / starter90 Tage90 / 180 / 365 / 730
enterprise / enterprise_trial365 Tage90 / 180 / 365 / 730

Die Obergrenze von 730 Tagen (≈2 Jahre) hält die Live-Tabelle audit_events begrenzt. Alles darüber hinaus ist Aufgabe der WORM-Stufe; brauchst du abfragbare Historie über 730 Tage hinaus, reihe vierteljährlich einen Datenexport ein und archiviere ihn in deinem SIEM.

Die Grenze gilt für die Spalten Akteur und fields_json: Zeilen, die älter als das Fenster sind, behalten Hash, Zeitstempel und Aktionsnamen (sodass Berichte für SOC 2 / DORA weiter funktionieren), aber der Akteur wird zu redacted pseudonymisiert und der Inhalt durch {} ersetzt. Die kryptografische Kette wird dadurch nicht unterbrochen; die Schwärzung ist eine Pseudonymisierung an Ort und Stelle, kein Löschen.

Das abfragbare Fenster ist die Klasse audit_payload der Aufbewahrung von Nachweisen, wo auch die anderen Nachweisklassen eingestellt werden. Eine Aufbewahrungspflicht (Legal Hold) setzt die Schwärzung aus.

Das abfragbare Fenster ändern

Mandanten-Admin → Sicherheit → Authentifizierungsrichtlinie → Karte Aufbewahrung des Audit-Logs. Wähle 90 / 180 / 365 / 730 Tage. Hinweise zum Verhalten:

  • Verlängern des Fensters (z. B. 90 → 365): Neu geschriebene Zeilen bleiben länger abfragbar. Zeilen, die unter dem vorherigen Fenster bereits geschwärzt wurden, bleiben geschwärzt; fremforge macht keine Schwärzung rückgängig.
  • Verkürzen des Fensters (z. B. 365 → 90): Der nächste tägliche Schwärzungslauf schwärzt jede Zeile, die älter als das neue Fenster ist. Eine angewendete Schwärzung lässt sich nicht rückgängig machen; die WORM-Kette ist davon nicht betroffen.

Zu dieser Einstellung erfasste Audit-Aktion: tenant.auth_policy.audit_retention.updated (mit den Tageszahlen previous und next).

Compliance-Zusagen

DPA Anhang A.7 sagt die Basis von 90 Tagen plus 3 Jahre WORM zu. Das Enterprise-SLA sagt 365 Tage abfragbar plus 3 Jahre WORM zu. Unter den Standard des Plans zu gehen, ist in der Oberfläche erlaubt, würde aber gegen den DPA verstoßen: fremforge erzwingt die Untergrenze nicht in der Spalte, das tut der Vertrag. Ein Ausfall des täglichen Schwärzungslaufs selbst (unabhängig davon, ob ein Kunde die Aufbewahrung ändert) wird laufend überwacht und alarmiert.