Data Lifecycle Data Lifecycle Management Version: 1.0 Date: 2026-02-21 Status: Approved Owner: Database Architect Overview Drop processes personal and financial data subject to multiple overlapping regulatory frameworks. This document defines retention periods, archival strategies, deletion cascades, and GDPR data subject request handling for all 19 tables. Applicable regulations: GDPR (Personopplysningsloven, LOV-2018-06-15-38) -- data minimization, right to erasure, right to access AML/KYC (Hvitvaskingsloven, LOV-2018-06-01-23) -- 5-year retention post-relationship Norwegian Bookkeeping Act (Bokforingsloven) -- 5-year retention for financial records PSD2 (Betalingstjenesteloven) -- audit trail requirements Finansavtaleloven -- complaint handling records Key tension: GDPR right to erasure (Art. 17) vs. AML legal retention obligations. AML wins -- data required for anti-money laundering must be retained for 5 years regardless of erasure requests. Retention Periods Per-Table Retention Schedule Table Retention Period Legal Basis Archival After Purge After users 5 years post-relationship end Hvitvaskingsloven section 30 Account deletion + 1 year 5 years post-deletion bank_accounts 5 years post-relationship end Hvitvaskingsloven section 30 Account deletion 5 years post-deletion transactions 5 years from transaction date Bokforingsloven section 13, Hvitvaskingsloven section 30 1 year after transaction 5 years after transaction recipients 5 years post-relationship end Hvitvaskingsloven section 30 (counterparty records) Account deletion 5 years post-deletion merchants 5 years post-relationship end Bokforingsloven, Hvitvaskingsloven Account deletion 5 years post-deletion sessions 90 days after expiry Legitimate interest (security) After expiry 90 days after expiry notifications 1 year from creation Legitimate interest (UX) 6 months 1 year settings Duration of relationship Contract performance Account deletion Immediate on deletion exchange_rates Indefinite (reference data) Legitimate interest Never Never cards 5 years post-cancellation PCI-DSS, Bokforingsloven Card cancellation 5 years post-cancellation spending_limits Duration of card lifecycle Contract performance Card cancellation With card record rate_limits Until window expires Legitimate interest (security) Auto-cleaned per request Immediate on expiry audit_log 5 years from event PSD2 Art. 94, Hvitvaskingsloven 1 year after event 5 years after event aml_alerts 5 years post-resolution Hvitvaskingsloven section 30 After resolution 5 years post-resolution str_reports 5 years after filing Hvitvaskingsloven section 30 Never (active reference) 5 years after filing screening_results 5 years post-relationship end Hvitvaskingsloven section 30 Account deletion 5 years post-deletion consents Duration of consent + 5 years GDPR Art. 7(1) (proof of consent) After withdrawal + 1 year 5 years after withdrawal data_access_requests 5 years from completion GDPR accountability (Art. 5(2)) After completion 5 years after completion complaints 5 years from resolution Finansavtaleloven, Bokforingsloven After resolution 5 years after resolution Per-Column Retention (Sensitive Fields) Table.Column Contains Retention Anonymization Method users.email PII (email address) Until erasure (then anonymized) Replace with deleted_usr_{hash}@anonymized.local users.first_name PII Until erasure Replace with [REDACTED] users.last_name PII Until erasure Replace with [REDACTED] users.phone PII Until erasure Replace with NULL users.date_of_birth PII Until erasure Replace with NULL users.national_id_hash PII (hashed) 5 years (AML) Already hashed; set to NULL after retention users.password_hash Auth credential Until erasure Replace with DELETED bank_accounts.account_number Financial PII 5 years (AML) Replace with ****{last4} bank_accounts.iban Financial PII 5 years (AML) Replace with ****{last4} recipients.bank_account Financial PII 5 years (AML) Replace with ****{last4} recipients.name PII 5 years (AML, counterparty) Replace with [REDACTED] cards.last_four Financial (partial) 5 years Already truncated cards.pin_hash Auth credential Until card cancellation Set to NULL audit_log.ip_address PII (IP address) 5 years (PSD2) Replace with 0.0.0.0 after retention audit_log.user_agent Quasi-PII 5 years Replace with [REDACTED] after retention consents.ip_address PII 5 years (proof of consent) Replace with 0.0.0.0 after retention Archival Strategy Active vs. Archived Data flowchart LR A[Active Data
Primary Database] -->|After retention trigger| B[Cold Archive
Read-only Storage] B -->|After full retention period| C[Purge
Permanent Deletion] subgraph "Active (PostgreSQL)" A1[Recent transactions] A2[Active users] A3[Current sessions] end subgraph "Cold Archive (S3/Glacier)" B1[Old transactions > 1 year] B2[Deleted user records] B3[Resolved AML alerts] B4[Filed STR reports] end subgraph "Purge" C1[Records past 5-year retention] C2[Anonymized analytics retained] end Archival Tiers Tier Storage Access Time Data Types Cost Hot (Active DB) PostgreSQL Milliseconds All current data, active users, recent transactions Primary DB cost Warm (Archive DB) PostgreSQL read replica or separate schema Seconds Transactions > 1 year, deleted users pending retention Reduced compute Cold (Object storage) AWS S3 / Glacier Minutes to hours Compliance exports, old audit logs, filed STR reports Minimal Archival Process Daily job: Identify records eligible for archival (past active retention period) Export: Write eligible records to archive storage (S3 with server-side encryption) Verify: Confirm archive integrity (checksum comparison) Remove from active: Delete from primary database Log: Record archival action in audit_log Deletion Cascades: User Account Deletion When a user requests account deletion (GDPR Art. 17 right to erasure), the following cascade executes: flowchart TD A[DELETE /api/user/account] --> B{Active transactions?} B -->|Yes, processing| C[Reject: Wait for completion] B -->|No| D[Begin deletion cascade] D --> E[Revoke all sessions] E --> F[Soft-delete user record] F --> G[Anonymize PII fields] G --> H[Create data_access_request
type=erasure, status=completed] subgraph "Immediate Actions" E F G end subgraph "Retained for AML (5 years)" I[transactions — amounts, dates, types] J[audit_log — anonymized entries] K[aml_alerts — if any] L[str_reports — if any] M[screening_results — if any] end subgraph "Deleted Immediately" N[settings — preferences] O[notifications — all] P[rate_limits — if any for user IP] end subgraph "Anonymized + Retained" Q[bank_accounts — account numbers masked] R[recipients — names redacted] S[consents — IP anonymized] end H --> I H --> J H --> K H --> N H --> Q Deletion Cascade Detail Step Table Action SQL 1 sessions Revoke all UPDATE sessions SET revoked = 1 WHERE user_id = ? 2 users Soft delete + anonymize UPDATE users SET deleted_at = CURRENT_TIMESTAMP, email = 'deleted_' || id || '@anonymized.local', first_name = '[REDACTED]', last_name = '[REDACTED]', phone = NULL, date_of_birth = NULL, password_hash = 'DELETED' WHERE id = ? 3 settings Delete DELETE FROM settings WHERE user_id = ? 4 notifications Delete DELETE FROM notifications WHERE user_id = ? 5 bank_accounts Anonymize UPDATE bank_accounts SET account_number = '****' || RIGHT(account_number, 4), iban = CASE WHEN iban IS NOT NULL THEN '****' || RIGHT(iban, 4) END WHERE user_id = ? 6 recipients Anonymize UPDATE recipients SET name = '[REDACTED]', bank_account = '****' || RIGHT(bank_account, 4) WHERE user_id = ? 7 consents Anonymize IP UPDATE consents SET ip_address = '0.0.0.0' WHERE user_id = ? 8 cards Anonymize UPDATE cards SET pin_hash = NULL WHERE user_id = ? 9 spending_limits Delete DELETE FROM spending_limits WHERE user_id = ? 10 data_access_requests Create record INSERT INTO data_access_requests (id, user_id, request_type, status, completed_at) VALUES (?, ?, 'erasure', 'completed', CURRENT_TIMESTAMP) 11 audit_log Log deletion INSERT INTO audit_log (id, user_id, action, details) VALUES (?, ?, 'user.deleted', '{"reason":"gdpr_erasure"}') NOT deleted (AML retention): transactions , audit_log (existing entries), aml_alerts , str_reports , screening_results , merchants . These are retained for 5 years per hvitvaskingsloven section 30, with PII fields anonymized. Data Subject Access Request (DSAR) Implementation DSAR Types Request Type GDPR Article SLA Implementation Export (right to access) Art. 15 30 days GET /api/user/data-export -- returns JSON with all user data Erasure (right to be forgotten) Art. 17 30 days DELETE /api/user/account -- soft delete + anonymization cascade Rectification (right to correct) Art. 16 30 days POST /v1/user/rectification -- updates specified fields, creates data_access_request record Restriction (right to restrict) Art. 18 30 days POST /v1/user/restriction -- flags account as restricted, creates data_access_request record Export Flow sequenceDiagram participant U as User participant API as API participant DB as Database U->>API: GET /api/user/data-export API->>DB: SELECT * FROM users WHERE id = ? API->>DB: SELECT * FROM transactions WHERE user_id = ? API->>DB: SELECT * FROM recipients WHERE user_id = ? API->>DB: SELECT * FROM bank_accounts WHERE user_id = ? API->>DB: SELECT * FROM settings WHERE user_id = ? API->>DB: SELECT * FROM consents WHERE user_id = ? API->>DB: INSERT INTO data_access_requests
(type='export', status='completed') API-->>U: 200 JSON { user, transactions, recipients, bankAccounts, settings, consents } The current implementation ( /api/user/data-export ) returns data inline as JSON. For production, large exports should be written to a temporary signed S3 URL and the download_url field in data_access_requests populated. DSAR Tracking All DSARs are tracked in the data_access_requests table: Field Purpose request_type export, erasure, rectification, restriction status pending -> processing -> completed/rejected requested_at When the user submitted the request completed_at When the request was fulfilled download_url Temporary URL for data export files notes Internal processing documentation Anonymization Techniques For Analytics Retention After the active retention period, data can be anonymized for analytics rather than deleted: Data Type Anonymization Technique Reversible? Analytics Value User identity Replace name/email with opaque ID No User-level metrics without PII Transaction amounts Retain exact values (not PII) N/A Revenue and volume analytics Geographic data Retain country codes only N/A Corridor analysis Timestamps Retain date, remove time Partially Trend analysis IP addresses Replace with 0.0.0.0 No None (removed for privacy) Bank account numbers Replace with ****{last4} No None Phone numbers Remove entirely No None Anonymization SQL Pattern -- Anonymize a deleted user's data for analytics retention UPDATE users SET email = 'anon_' || id || '@analytics.internal', first_name = '[ANON]', last_name = '[ANON]', phone = NULL, date_of_birth = NULL, national_id_hash = NULL, password_hash = 'ANONYMIZED' WHERE id = ? AND deleted_at IS NOT NULL; -- Transaction data is retained as-is (amounts are not PII) -- Recipient names are redacted UPDATE recipients SET name = 'Recipient_' || id, bank_account = '****' || SUBSTR(bank_account, -4) WHERE user_id = ?; Legal Basis Reference Retention Obligation Law Section Requirement KYC/AML records Hvitvaskingsloven Section 30 Retain customer identity and transaction records for 5 years after relationship ends Transaction records Bokforingsloven Section 13 Retain accounting records for 5 years (3.5 years primary, 1.5 years secondary) Audit trail PSD2 / Betalingstjenesteloven Art. 94 impl. Maintain records of payment transactions for at least 5 years Consent proof GDPR Art. 7(1) Demonstrate that consent was given (retain proof) Complaint records Finansavtaleloven Section 3-53 Maintain complaint records (15 business day response SLA) Right to erasure exceptions GDPR Art. 17(3)(b) Erasure does not apply when processing is necessary for compliance with legal obligation Data minimization GDPR Art. 5(1)(c) Do not retain data longer than necessary for stated purpose STR records Hvitvaskingsloven Section 30 STR reports and supporting documentation retained 5 years after filing Conflict resolution: When GDPR right to erasure conflicts with AML retention requirements, AML wins per GDPR Art. 17(3)(b). The user is informed that "data [is] retained for 5 years per AML requirements" in the deletion response. Automated Lifecycle Jobs Job Frequency Action Session cleanup Daily Delete expired sessions older than 90 days Rate limit cleanup Every 100 rate limit checks Delete expired rate limit entries (implemented in middleware/rate-limit.ts ) Notification cleanup Weekly Archive notifications older than 6 months, delete older than 1 year Audit log archival Monthly Move audit entries older than 1 year to cold storage AML alert archival Monthly Archive resolved alerts older than 1 year User data purge Monthly Permanently delete anonymized user data past 5-year retention Consent proof archival Monthly Archive withdrawn consents older than 1 year Retention Cron Endpoint The retention enforcement is implemented as GET /v1/cron/retention (see cron.ts ). When triggered, it: User anonymization (5+ years post-deletion): Anonymizes PII fields ( email , first_name , last_name , phone , date_of_birth , national_id_hash , password_hash ) for users deleted more than 5 years ago Session cleanup : Deletes expired sessions older than 90 days OTP cleanup : Removes expired OTP codes (legacy table, wrapped in try/catch) This endpoint should be called periodically (e.g., daily via external scheduler or cron job). It is not automatically scheduled within the application. Cross-References Database schema: DATABASE-SCHEMA.md Database design: database-design.md Audit architecture: audit-architecture.md Compliance status: COMPLIANCE.md Security architecture: SECURITY-ARCHITECTURE.md GDPR API endpoints: API-REFERENCE.md (GDPR & Compliance section) Account deletion: DELETE /api/user/account in API-REFERENCE.md Data export: GET /api/user/data-export in API-REFERENCE.md