/** * Closed enum of audited actions per DPA. New action types require an * explicit type bump — compliance auditors sample by enum value. */ export type AuditAction = | "VIEW" | "CREATE" | "UPDATE" | "DELETE" | "EXPORT" | "PERMISSION_CHANGE"; /** * `from_where` fragment per DPA. IP truncated to /24 (IPv4) or /48 (IPv6) * before storage; use `truncateIp(rawIp)` to enforce. For non-HTTP contexts, * sentinels are conventional: `{ ipTruncated: "system", userAgent: "background-job" }`. */ export type AuditFrom = { ipTruncated: string; userAgent: string; }; /** * Universal audit entry. By construction, this type has NO `payload`/`body`/ * `oldValue`/`newValue` fields — the DPA "what NOT to log" exclusion list is * enforced by the type itself. UPDATE actions capture field NAMES only * (`changedFields`); per-collection value capture is a separate API (out of * scope for v1). */ export type AuditEntry = { // WHO /** User id, or "system"/"service-{name}" for non-user actors. NEVER email or name. */ actorId: string; actorType: "user" | "system" | "service"; /** Snapshot of actor's roles AT TIME OF ACTION — preserves historical state. */ actorRoles: string[]; // WHAT action: AuditAction; resource: { type: string; id?: string }; /** UPDATE only: names of fields that changed (NOT values — PII risk). */ changedFields?: string[]; // WHEN /** Server time. Sinks serialize as ISO 8601. */ at: Date; // SCOPE (where) scope: { feature: string; environment: string; /** Required field. Single-tenant projects use "default" as the sentinel. */ tenant: string; }; // WHY reason?: string; /** OTel trace ID. Auto-populated by `TraceIdEnrichingAuditLog` decorator at bind time. */ correlationId?: string; requestId?: string; // FROM (per DPA) from: AuditFrom; // PII CLASSIFICATION /** Caller MUST declare. Drives downstream retention/access policies. */ containsPii: boolean; /** * Free-form list. Conventions (suggested, not enforced): "email", "name", * "phone", "address", "ssn", "financial", "health". Free-form because * regulatory categories differ by jurisdiction. */ piiCategories?: string[]; // OUTCOME outcome: "success" | "denied" | "error"; errorCode?: string; };