# Data Retention

How long each kind of data lives + how it's purged.

| Data                               | Retention                           | Purge mechanism                | Reason                                  |
| ---------------------------------- | ----------------------------------- | ------------------------------ | --------------------------------------- |
| `User` (active)                    | Indefinite while active             | N/A                            | Operational need                        |
| `User` (deactivated, soft-deleted) | 90 days then anonymize              | Cron + `anonymize_user.sql`    | PDPA §32 / GDPR Art. 17                 |
| `RefreshToken`                     | 30 days from issuance               | Cron deletes expired + revoked | Auth design                             |
| `AuditLog`                         | 2 years                             | Cron after 730 days            | Legal — Thai accounting + PDPA evidence |
| `Document` (active)                | Indefinite while owner active       | N/A                            | Business need                           |
| `Document` (soft-deleted)          | 30 days then hard delete blob + row | Cron                           | Storage cost + privacy                  |
| `Announcement` (expired)           | 1 year then hard delete             | Cron                           | Cleanup                                 |
| `ApprovalRequest` (terminal)       | 7 years                             | Cron after 2555 days           | Legal — financial decisions kept by law |
| Application logs                   | 30 days hot, 1 year cold            | Loki retention                 | Operational + investigation             |
| OpenTelemetry traces               | 7 days                              | Tempo retention                | Operational                             |
| Backups                            | 30 days rolling                     | Backup tool TTL                | Disaster recovery                       |

## Cron jobs (Phase 8+ wiring)

Implemented as scheduled jobs in `apps/worker-nest`:

- `purge-expired-refresh-tokens` — daily 02:00 Asia/Bangkok
- `purge-deactivated-users` — weekly Sunday 03:00
- `purge-soft-deleted-documents` — daily 03:30
- `purge-expired-announcements` — daily 04:00
- `purge-old-audit-logs` — monthly first day 04:30

Each job:

- Logs an `audit_logs` row (`action: 'retention.purge.<entity>'`) summarizing what was removed.
- Anonymizes rather than deletes when FKs need to stay (users).

## Special holds

Legal hold overrides retention. Implement as `User.legal_hold` / `Document.legal_hold` boolean — purges skip rows with the flag set. Holds must be set via an audited admin endpoint, not by raw SQL.

## Cross-references

- [`pdpa.md`](pdpa.md), [`gdpr.md`](gdpr.md) — legal basis for the windows
- [`right-to-erasure-runbook.md`](right-to-erasure-runbook.md) — on-demand erasure
- `database/scripts/anonymize/anonymize_user.sql` — the anonymization implementation
