# Audit SDK

<Lead>
**`platform.audit`** ist der unveränderliche Audit-Trail des Tenants. Jede sicherheits- oder abrechnungsrelevante Aktion hinterlässt einen Eintrag: wer hat wann was mit welcher Ressource gemacht.
</Lead>

## Schreiben

In App-Code gibt es den fail-soften Wrapper `recordTenantAudit`: ein fehlgeschlagener Audit-Write wird geloggt (PII-frei), bricht aber nie die Nutzer-Aktion ab. Darunter liegt `createAuditService(db).log`:

```typescript
import { recordTenantAudit } from "@/lib/audit";

await recordTenantAudit(ctx, {
  action: "homestaging.generation.created",   // Namenskonvention, siehe unten
  resourceType: "hs_generation",              // optional
  resourceId: generation.id,                  // optional
  metadata: { style: "scandinavian" },        // optional, NIE PII oder Geheimnisse
});
// userId, ipAddress, userAgent kommen automatisch aus ctx.
```

Das Log ist **append-only**: die Tabelle hat unter RLS eine reine Lese-Policy, der Insert läuft SDK-intern mit erhöhter Rolle. App- und Modul-Code nennt nie selbst eine erhöhte Rolle (der Secrets-Validator erzwingt das).

## Namenskonvention

- **Modul-Actions:** `<modul>.<entity>.<verb>` in Vergangenheitsform, z.B. `credentials.template.created`, `credentials.policy.updated`, `homestaging.generation.created`. Definiere die Strings als Konstanten an einer Stelle im Modul, damit Log und Abfragen nie auseinanderlaufen.
- **Plattform-Actions** (Bestand) nutzen `snake_case` wie `member_invited`, `share_viewed`. Für neue Modul-Events gilt die Punkt-Konvention.
- Action-Strings sind API: einmal geschrieben, werden sie nie umbenannt (Abfragen und Auswertungen filtern exakt auf den String).

## Metadaten-Regeln

- **Nie PII:** keine E-Mail-Adressen, Namen, Nutzereingaben oder Datei-Inhalte in `metadata`.
- **Nie Klartext-Geheimnisse:** keine Passwörter, Tokens oder Keys, auch nicht "nur zum Debuggen".
- Gut geeignet: IDs, Enum-Werte, Zähler, Flags. Der Eintrag soll die Frage "was ist passiert" beantworten, nicht den Inhalt duplizieren.
- `resourceType`/`resourceId` referenzieren die betroffene Ressource; wer eine Kategorie braucht, legt sie in `metadata` ab.

## Lesen

`query(ctx, input)` liefert den Trail des eigenen Tenants, RLS-scoped, neueste zuerst, mit Keyset-Cursor:

```typescript
import { queryTenantAudit } from "@/lib/audit";

const { entries, nextCursor } = await queryTenantAudit(ctx, {
  action: "credentials.secret.revealed",   // optional: exakter Action-String
  userId,                                  // optional: handelnde Person
  resourceType: "credential",              // optional
  fromDate, toDate,                        // optional: Zeitfenster
  limit: 50,                               // max 100
});
```

## Wo das Log sichtbar ist

Tenant-Admins sehen den Trail in der Plattform unter **Einstellungen > Aktivität**. Deine Einträge sind dort unmittelbar sichtbar; schreibe `action`-Strings und Metadaten so, dass sie einer Admin-Prüfung standhalten.

## Weiterführend

Audit beantwortet "wer hat was getan" für den Tenant selbst. Sollen **externe Systeme** auf Ereignisse reagieren, emittierst du zusätzlich ein Domain-Event: [Webhooks und Events](/dev/sdk/webhooks-und-events).
