Files
agentic-dev/packages/core-shared/src/audit/audit-entry.ts

79 lines
2.3 KiB
TypeScript

/**
* 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;
};