cedc8d79ee
Renumbers the four 2026-08-07 slices (ApprovalWorkflow, AuditTrail, TradeExecution, PortfolioReconciliation) to previously-unused VS-26..29, leaving WBS_MASTER.csv's original VS-03/04/12/14 definitions (IngestMarketDataPIT, ApplyCorporateActions, RankBuyCandidates, GenerateDailyRecommendations) untouched, per docs/DECISIONS/ADR-WBS-001-slice-renumbering.md. While investigating, found two things not yet resolved by this commit: - DEBT-017 (duplicate ApprovalWorkflow implementation): the tested backend (ApprovalWorkflow/) is [DontRegister]'d dead code; the live one (Features/ApprovalWorkflow/, wired in Program.cs) has no dedicated tests. AEG-VS-26-01 downgraded from COMPLETED to BLOCKED in the tracker pending an architect decision on which implementation is canonical. - Features/MarketData and Features/Portfolio (VS-03/04/05/08 Market Data Ingestion Dashboard, Portfolio Rebalance, Risk Metrics, Dashboard) are a third, already-implemented-and-tested body of work entirely absent from WBS_PROGRESS_TRACKER.csv. Flagged in CURRENT_ROADMAP.md as a follow-up. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
256 lines
8.3 KiB
Markdown
256 lines
8.3 KiB
Markdown
# VS-27: Immutable Audit Trail (GDPR/Compliance)
|
|
|
|
**Vertical Slice:** VS-27 (Audit Log & Compliance Trail)
|
|
**Version:** 1.0 COMPLETE
|
|
**Date:** 2026-08-07
|
|
**Owner:** Compliance + Security
|
|
**Status:** ✅ READY FOR IMPLEMENTATION
|
|
**Depends On:** VS-02/03 (governance foundation) ✅ COMPLETE
|
|
|
|
---
|
|
|
|
## 📋 User Story
|
|
|
|
**As a** compliance officer / auditor
|
|
**I want to** maintain immutable audit trail of all model operations
|
|
**So that** we can satisfy regulatory audits (FSS, GDPR, PCI-DSS) and forensically investigate issues
|
|
|
|
**Acceptance Criteria:**
|
|
- ✅ All model operations logged: create, approve, activate, deactivate, sell decision
|
|
- ✅ Audit events immutable: INSERT-only, no UPDATE/DELETE
|
|
- ✅ Event data: timestamp, actor, action, model_id, result, evidence links
|
|
- ✅ GDPR: Right-to-be-forgotten handling for customer data
|
|
- ✅ Retention: 7 years (regulatory requirement)
|
|
- ✅ Compliance: Links to approval evidence, PBO/DSR, backtest reports
|
|
|
|
---
|
|
|
|
## 🎯 Non-Goals
|
|
|
|
- ❌ Real-time alerting on suspicious activity (belongs to separate monitoring slice)
|
|
- ❌ Machine learning for anomaly detection (deferred)
|
|
- ❌ Custom compliance report generation (belongs to reporting slice)
|
|
- ❌ Encryption of audit logs at rest (assume PostgreSQL encryption)
|
|
|
|
---
|
|
|
|
## 📊 Data Schema
|
|
|
|
```sql
|
|
-- Audit trail (immutable, INSERT-only)
|
|
CREATE TABLE compliance.audit_events (
|
|
id UUID PRIMARY KEY,
|
|
event_type VARCHAR(100) NOT NULL, -- MODEL_CREATED, APPROVAL_PROPOSED, APPROVAL_APPROVED, MODEL_ACTIVATED, SELL_DECISION_MADE, SELL_EXECUTED, etc.
|
|
entity_type VARCHAR(50) NOT NULL, -- MODEL, APPROVAL, SELL_DECISION, TRADE_EXECUTION
|
|
entity_id UUID NOT NULL, -- model_id, approval_id, decision_id, trade_id
|
|
actor_email VARCHAR(255) NOT NULL, -- Who performed the action
|
|
actor_role VARCHAR(50), -- MAKER, CHECKER, SRE, SYSTEM
|
|
event_at TIMESTAMPTZ NOT NULL, -- When action occurred
|
|
result VARCHAR(50) NOT NULL, -- SUCCESS, FAILURE, PARTIAL
|
|
error_message TEXT, -- If FAILURE, what went wrong
|
|
details JSONB, -- Event-specific metadata (e.g., model version, approval notes)
|
|
evidence_links TEXT[], -- Array of evidence artifact URLs (S3, logs, reports)
|
|
ip_address INET, -- Source IP for security analysis
|
|
user_agent TEXT, -- Client identifier
|
|
published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
correlation_id UUID NOT NULL, -- Links to related events
|
|
revision INT NOT NULL DEFAULT 1
|
|
);
|
|
|
|
-- GDPR: Personal data retention tracker
|
|
CREATE TABLE compliance.gdpr_retention (
|
|
id UUID PRIMARY KEY,
|
|
event_id UUID NOT NULL REFERENCES compliance.audit_events(id),
|
|
customer_id UUID, -- Links to personal data
|
|
data_categories VARCHAR(50)[], -- PII, EMAIL, TRADING_HISTORY, etc.
|
|
retention_ends_at DATE, -- When to purge
|
|
purge_status VARCHAR(50), -- PENDING, PURGED, EXCEPTION
|
|
published_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 🔐 Event Types Logged
|
|
|
|
| Event | Trigger | Logged By | Details |
|
|
|-------|---------|-----------|---------|
|
|
| MODEL_CREATED | New model version | System | model_id, algorithm, version |
|
|
| MODEL_ARCHIVED | Model retired | SRE | model_id, reason |
|
|
| APPROVAL_PROPOSED | Maker submits proposal | Maker | approval_id, model_id, justification |
|
|
| APPROVAL_APPROVED | Checker signs off | Checker | approval_id, evidence_links, notes |
|
|
| APPROVAL_REJECTED | Checker rejects | Checker | approval_id, rejection_reason |
|
|
| MODEL_ACTIVATED | SRE activates model | SRE | model_id, effective_at, approval_id |
|
|
| MODEL_DEACTIVATED | SRE deactivates | SRE | model_id, reason |
|
|
| SELL_DECISION_MADE | Engine generates sell signal | System | decision_id, model_id, signal_strength |
|
|
| SELL_EXECUTED | Trade executed | System | trade_id, quantity, price, model_id |
|
|
| BACKTEST_COMPLETED | Shadow run finishes | System | job_id, oos_score, pbo_score, dsr |
|
|
| DATA_CORRECTION | Source data corrected | Data Gov | entity_id, old_value, new_value |
|
|
| COMPLIANCE_AUDIT | Auditor reviews trail | Auditor | audit_scope, findings, escalation |
|
|
|
|
---
|
|
|
|
## 🔄 GDPR Compliance Flow
|
|
|
|
### Right-to-Be-Forgotten (Article 17)
|
|
|
|
**Scenario:** Customer requests deletion of personal data
|
|
**Process:**
|
|
|
|
1. **Identify:** Find all audit_events linked to customer_id
|
|
2. **Redact:**
|
|
- Mark email addresses → `<redacted>`
|
|
- Mark customer IDs → `<purged>`
|
|
- Keep event_type, correlation_id for forensics
|
|
3. **Retain:** Keep anonymized event log for 7 years (legal requirement)
|
|
4. **Verify:** Confirm no personal data remains via compliance.gdpr_retention
|
|
|
|
**Implementation:**
|
|
```sql
|
|
-- Mark GDPR retention as PURGED (no actual deletion)
|
|
UPDATE compliance.gdpr_retention
|
|
SET purge_status = 'PURGED', retention_ends_at = NOW()
|
|
WHERE customer_id = $1;
|
|
|
|
-- Redact personal data in audit_events (soft delete)
|
|
UPDATE compliance.audit_events
|
|
SET details = jsonb_set(details, '{actor_email}', '"<redacted>"'::jsonb)
|
|
WHERE entity_id IN (SELECT id FROM ... WHERE customer_id = $1);
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 API Contract (Query-Only)
|
|
|
|
### GET /audit/events (Compliance Officer)
|
|
|
|
**Query Params:**
|
|
- `entityId=uuid` (filter by entity)
|
|
- `eventType=MODEL_ACTIVATED` (filter by event)
|
|
- `dateFrom=2026-01-01&dateTo=2026-12-31` (date range)
|
|
- `actorEmail=user@company.com` (who performed action)
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"items": [
|
|
{
|
|
"id": "event-uuid",
|
|
"eventType": "MODEL_ACTIVATED",
|
|
"entityId": "model-uuid",
|
|
"actorEmail": "sre@company.com",
|
|
"eventAt": "2026-08-07T10:00:00Z",
|
|
"result": "SUCCESS",
|
|
"evidenceLinks": ["s3://evidence/pbo-report.json"],
|
|
"correlationId": "correlation-uuid"
|
|
}
|
|
],
|
|
"total": 1,
|
|
"pages": 1
|
|
}
|
|
```
|
|
|
|
### GET /audit/events/{id} (Full Detail)
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"id": "event-uuid",
|
|
"eventType": "MODEL_ACTIVATED",
|
|
"entityType": "MODEL",
|
|
"entityId": "model-uuid",
|
|
"actorEmail": "sre@company.com",
|
|
"actorRole": "SRE",
|
|
"eventAt": "2026-08-07T10:00:00Z",
|
|
"result": "SUCCESS",
|
|
"details": {
|
|
"modelId": "model-uuid",
|
|
"modelVersion": "1.0.0",
|
|
"effectiveAt": "2026-09-15",
|
|
"approvalId": "approval-uuid"
|
|
},
|
|
"evidenceLinks": [
|
|
"s3://evidence/pbo-report.json",
|
|
"s3://evidence/oos-backtest.csv"
|
|
],
|
|
"ipAddress": "192.168.1.100",
|
|
"userAgent": "PostmanRuntime/7.32.3",
|
|
"publishedAt": "2026-08-07T10:00:00Z",
|
|
"correlationId": "correlation-uuid"
|
|
}
|
|
```
|
|
|
|
### POST /compliance/gdpr-request (Customer Data Deletion)
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"customerId": "customer-uuid",
|
|
"requestDate": "2026-08-07",
|
|
"reason": "Right to be forgotten (GDPR Article 17)"
|
|
}
|
|
```
|
|
|
|
**Response (202 Accepted):**
|
|
```json
|
|
{
|
|
"gdprTrackingId": "gdpr-uuid",
|
|
"status": "IN_PROGRESS",
|
|
"estimatedCompletion": "2026-08-08T12:00:00Z"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## ✅ Governance Gates
|
|
|
|
### Pre-Merge Gates
|
|
|
|
- [x] **Event Schema:** All model operations mapped to audit_events
|
|
- [x] **Immutability:** INSERT-only, no UPDATE/DELETE
|
|
- [x] **GDPR Handling:** Redaction logic for personal data
|
|
- [x] **Retention Policy:** 7-year retention for compliance
|
|
- [x] **Audit Query API:** Read-only endpoints for compliance officers
|
|
|
|
### Post-Merge Validation (Deferred)
|
|
|
|
- [ ] Integration tests (event logging on model operations)
|
|
- [ ] GDPR purge tests (verify data redaction)
|
|
- [ ] Audit report generation (7-year retention query)
|
|
|
|
---
|
|
|
|
## 🛡️ Security & Compliance
|
|
|
|
**Immutability Guarantees:**
|
|
- INSERT-only table (no UPDATE, no DELETE)
|
|
- Timestamp cannot be modified after insertion
|
|
- Correlation_id immutable (traceability)
|
|
|
|
**Regulatory Requirements:**
|
|
- ✅ FSS (금감원): Audit trail for 7 years (model_operations)
|
|
- ✅ GDPR: Right-to-be-forgotten handling (redaction, not deletion)
|
|
- ✅ PCI-DSS: IP address + user agent logged (for forensics)
|
|
- ✅ Internal Compliance: Evidence linkage (PBO/DSR/OOS artifacts)
|
|
|
|
**Access Control:**
|
|
- Compliance Officer: Read-only access to all events
|
|
- Auditor: Query with date range filters
|
|
- System: Automatic event logging (no manual entry)
|
|
- Data Admin: GDPR purge operation (privileged, logged itself)
|
|
|
|
---
|
|
|
|
## 📋 Related Specifications
|
|
|
|
- **VS-00:** PIT envelope (published_at, correlation_id, revision)
|
|
- **VS-02:** Governance foundation (data sources, policies)
|
|
- **VS-26:** Approval workflow (events logged by VS-27)
|
|
- **Compliance:** GDPR, FSS, PCI-DSS requirements
|
|
|
|
---
|
|
|
|
**Co-Authored-By:** Claude Haiku 4.5 <noreply@anthropic.com>
|
|
**Status:** ✅ READY FOR IMPLEMENTATION
|
|
**Next:** Phase 2 implementation (after F PR merged)
|