Domain Specification: Audit & Governance¶
Validated against PRD v1.0 — All FR-AU-* requirements. See Traceability Matrix.
1. Context Overview¶
Bounded Context: audit
Responsibility: Immutable append-only event log. Deterministic replay. Segregation of duties enforcement.
Owns: AuditEvent aggregate.
Consumed By: ALL bounded contexts (write events), Auditors (read/replay).
Events Published: AuditEventRecorded.
2. Event Model¶
data class AuditEvent(
val eventId: Long, // BIGSERIAL — sequential within partition
val correlationId: UUID, // links events across a single request
val eventType: EventType, // STATE_TRANSITION, MODULE_INVOCATION, DECISION, CONFIG_CHANGE, DATA_ACCESS, AUTH
val actorType: ActorType, // USER, SYSTEM
val actorId: UUID?,
val contextType: ContextType, // CUSTOMER, CASE, WORKFLOW
val contextId: UUID,
val action: String, // "State transition: ANALYST_REVIEW → EDD_REVIEW"
val payload: JsonNode, // full event data
val configVersion: String?,
val timestamp: Instant
)
enum class EventType { STATE_TRANSITION, MODULE_INVOCATION, DECISION, CONFIG_CHANGE, DATA_ACCESS, AUTH }
enum class ActorType { USER, SYSTEM }
enum class ContextType { CUSTOMER, CASE, WORKFLOW, CONFIG }
3. What Gets Logged¶
| Action | EventType | Payload Includes |
|---|---|---|
| Workflow state transition | STATE_TRANSITION | fromState, toState, workflowInstanceId, trigger |
| Module invocation (screening, risk, network) | MODULE_INVOCATION | moduleType, request payload, response payload, executionTimeMs, configVersion |
| Human decision (approve, reject, adjudicate) | DECISION | decisionType, actor, rationale, evidenceRefs, configVersion |
| Configuration change | CONFIG_CHANGE | configVersion, changedBy, changeSummary, oldValues, newValues |
| Sensitive data access | DATA_ACCESS | userId, accessedFields, customerId, ipAddress |
| Authentication | AUTH | userId, success/failure, ipAddress, reason (if failed) |
Events are write-only. No UPDATE, no DELETE. Immutable from the moment of recording.
4. API Contracts¶
4.1 Record Event (Internal)¶
POST /api/v1/audit/events
Authorization: System
Request:
{
"correlationId": "uuid",
"eventType": "DECISION",
"actorType": "USER",
"actorId": "user-uuid",
"contextType": "CASE",
"contextId": "case-uuid",
"action": "Onboarding decision: APPROVED",
"payload": { "decisionType": "APPROVED", "rationale": "...", "configVersion": "1.0.3" }
}
Response 201: { "eventId": 14932 }
4.2 Replay Case (Auditor)¶
GET /api/v1/audit/replay?contextType=CASE&contextId={caseId}&from=2024-01-01&to=2025-06-01&page=1&limit=100
Authorization: Bearer <jwt> (AUDITOR role)
Response 200:
{
"contextType": "CASE",
"contextId": "case-uuid",
"events": [
{ "eventId": 14200, "timestamp": "2025-06-01T10:00:00Z", "eventType": "STATE_TRANSITION", "action": "CREATED → ASSIGNED", "actor": "System" },
{ "eventId": 14201, "timestamp": "2025-06-01T10:05:00Z", "eventType": "MODULE_INVOCATION", "action": "Name Screening completed", "actor": "System" },
{ "eventId": 14250, "timestamp": "2025-06-01T13:00:00Z", "eventType": "DECISION", "action": "APPROVED", "actor": "Jane Doe (KYC Analyst)", "payload": { "rationale": "..." } }
],
"summary": {
"totalEvents": 52,
"decisions": [{ "type": "APPROVED", "actor": "Jane Doe", "configVersion": "1.0.3" }],
"moduleInvocations": 3,
"stateTransitions": 8
}
}
4.3 Export Audit Package¶
POST /api/v1/audit/export
Authorization: Bearer <jwt> (AUDITOR role)
Request: { "contextType": "CASE", "contextId": "uuid", "format": "PDF" }
Response 200: Binary PDF with full reconstruction.
5. Segregation of Duties¶
Enforced at the application layer:
| Rule | Enforcement |
|---|---|
| Case creator ≠ approver | Checked on decision: if case.createdBy == requestingUser → 403 Forbidden |
| Analyst ≠ FCC reviewer for same case | Checked on review: if case.lastAnalyst == requestingUser → 403 Forbidden |
| Auditor = read-only | Role has no POST/PATCH/DELETE permissions on any domain endpoint |
| Override requires secondary approval | Override decision creates PENDING_REVIEW task for second approver |
All SoD violations are logged as audit events with eventType=DECISION, action="SoD VIOLATION ATTEMPT", payload={actor, attemptedAction, blockedAt}.
6. Retention¶
- Active: 7 years from event timestamp (NFR-C01). Partitioned by month.
- Archived: After 7 years, partition detached and moved to cold storage. Retrievable within 30 days.
- Deletion: Never automatically deleted. Manual deletion requires break-glass procedure with dual approval.
Spec validated against PRD v1.0 requirements FR-AU-01 through FR-AU-03.