-- Workstream I: VS-04 Audit Trail (Immutable events + GDPR compliance) -- Creates compliance audit trail for model operations, regulatory reporting, and GDPR redaction CREATE SCHEMA IF NOT EXISTS compliance; -- Audit events (immutable, INSERT-only) CREATE TABLE IF NOT EXISTS 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, BACKTEST_COMPLETED, DATA_CORRECTION, etc. entity_type VARCHAR(50) NOT NULL, -- MODEL, APPROVAL, SELL_DECISION, TRADE_EXECUTION entity_id UUID NOT NULL, actor_email VARCHAR(255) NOT NULL, actor_role VARCHAR(50), -- MAKER, CHECKER, SRE, SYSTEM event_at TIMESTAMPTZ NOT NULL, result VARCHAR(50) NOT NULL, -- SUCCESS, FAILURE, PARTIAL error_message TEXT, details JSONB, -- Event-specific metadata evidence_links TEXT[], -- S3 artifact URLs (PBO scores, OOS returns, backtest reports) ip_address INET, -- Source IP for forensics user_agent TEXT, -- Client identifier published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), correlation_id UUID NOT NULL, -- Links related events revision INT NOT NULL DEFAULT 1 ); -- Indexes for compliance querying CREATE INDEX IF NOT EXISTS idx_audit_events_entity_id ON compliance.audit_events(entity_id); CREATE INDEX IF NOT EXISTS idx_audit_events_event_type ON compliance.audit_events(event_type); CREATE INDEX IF NOT EXISTS idx_audit_events_actor_email ON compliance.audit_events(actor_email); CREATE INDEX IF NOT EXISTS idx_audit_events_event_at ON compliance.audit_events(event_at); CREATE INDEX IF NOT EXISTS idx_audit_events_correlation_id ON compliance.audit_events(correlation_id); -- GDPR retention tracking (personal data retention policy) CREATE TABLE IF NOT EXISTS 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, PORTFOLIO_DATA, etc. retention_ends_at DATE, -- When to purge purge_status VARCHAR(50) NOT NULL DEFAULT 'PENDING', -- PENDING, PURGED, EXCEPTION purged_at TIMESTAMPTZ, exception_reason TEXT, published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), revision INT NOT NULL DEFAULT 1 ); -- Indexes for GDPR processing CREATE INDEX IF NOT EXISTS idx_gdpr_retention_customer_id ON compliance.gdpr_retention(customer_id); CREATE INDEX IF NOT EXISTS idx_gdpr_retention_purge_status ON compliance.gdpr_retention(purge_status); -- Event types enumeration (reference, not enforced at DB level) CREATE TABLE IF NOT EXISTS compliance.audit_event_types ( event_type VARCHAR(100) PRIMARY KEY, description TEXT, entity_type VARCHAR(50), -- MODEL, APPROVAL, SELL_DECISION, TRADE_EXECUTION created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- Seed event types INSERT INTO compliance.audit_event_types (event_type, description, entity_type) VALUES ('MODEL_CREATED', 'New model version created', 'MODEL'), ('MODEL_ARCHIVED', 'Model retired from use', 'MODEL'), ('APPROVAL_PROPOSED', 'Maker submitted activation proposal', 'APPROVAL'), ('APPROVAL_APPROVED', 'Checker approved proposal', 'APPROVAL'), ('APPROVAL_REJECTED', 'Checker rejected proposal', 'APPROVAL'), ('MODEL_ACTIVATED', 'SRE activated model in production', 'MODEL'), ('MODEL_DEACTIVATED', 'SRE deactivated model', 'MODEL'), ('SELL_DECISION_MADE', 'Signal engine generated sell signal', 'SELL_DECISION'), ('SELL_EXECUTED', 'Trade executed based on signal', 'TRADE_EXECUTION'), ('BACKTEST_COMPLETED', 'Shadow run/backtest finished', 'MODEL'), ('DATA_CORRECTION', 'Source data corrected retroactively', 'MODEL'), ('COMPLIANCE_AUDIT', 'Auditor reviewed trail', 'MODEL') ON CONFLICT (event_type) DO NOTHING; -- Schema ownership ALTER TABLE compliance.audit_events OWNER TO kartsell; ALTER TABLE compliance.gdpr_retention OWNER TO kartsell; ALTER TABLE compliance.audit_event_types OWNER TO kartsell; -- Immutability constraints (enforced via code, not DB triggers) -- INSERT-only: no UPDATE, no DELETE permitted on audit_events -- Timestamps: immutable after insertion (enforced in application layer) -- Correlation_id: immutable for traceability -- 7-year retention policy (FSS requirement) -- retention_ends_at defaults to now() + 7 years (enforced in application)