Revision history
- v1 — 2026-05-01 — Original: 12 months active + indefinite archive.
- v2 — 2026-05-11 (CONV-34) — Amended by Phase 1.3 sub-plan business review (Legal HIGH 2 + Security HIGH 3). Retention bounded to 7 years (Cyprus statutory limitation period for tax/contract claims) with pseudonymisation of
actor_user_idon the 12-month archive boundary. Append-only DB role + monthly hash-chain seal + off-box archive shipping mandatory Stage 1. Hardware WORM media deferred Stage 2.
Context
The operator dashboard (Phase 1.4.7) and tenant audit surfaces (Phase 1.3 § 2.4) include an audit log of studio/user actions. Retention period affects:
- Storage cost — at projected 1.8–7.3M rows/year (Phase 1.3 § Architectural risks), indefinite retention grows unboundedly.
- GDPR Art. 5(1)(e) data minimisation + Art. 17 erasure rights — indefinite PII retention conflicts with both. Legal HIGH finding from CONV-33 business review.
- Tamper evidence — append-only at application layer is insufficient if DB credentials are compromised. Security HIGH finding from CONV-33 business review.
- Statutory floor — Cyprus contract law (Limitation of Actions Law, Cap. 15 + 2012/66 amendment) sets a 6-year limitation period for contract claims; 7 years gives 1-year buffer + aligns with Cyprus tax records retention.
- Audit/investigation utility — billing disputes, fraud investigation, regulatory subpoena.
Decision
Three-stage retention model:
- 0–12 months — Active retention. Full row in primary
audit_eventstable with all metadata (incl.actor_user_id, IP, user_agent). Queryable by operator dashboard + tenant audit surfaces. - 12 months – 7 years — Archive (pseudonymised). Rows shipped to off-box S3 (Object Lock Compliance mode) in a separate AWS account, write-only cross-account IAM.
actor_user_idreplaced byhmac_sha256(user_id || global_pepper). Originaluser_id ↔ hashmapping kept in a separate vault DB with operator dual-approval access (fraud investigation path).sensitivePII fields in JSONB metadata (per Phase 1.3 § 2.4.Cpii_class) redacted on shipping;personal_contentfields kept (may be re-pseudonymised Stage 2). - After 7 years — Hard delete. Both archive object + vault mapping row purged (scheduled deletion job; audited in operator log). Hard-deletion event itself logged in an out-of-band compliance log.
Immutability + tamper-evidence (Stage 1 mandatory):
- Insert-only DB role — application connects via
audit_writerPostgres role withINSERTgrant only onaudit_events. NoUPDATE/DELETE/TRUNCATE. Schema migrations + archival jobs run under separateaudit_adminrole, credentials rotated via secrets manager. - Monthly hash-chain seal — cron job at the end of each month computes
seal_hash = sha256(prev_seal_hash || rows_concat_sorted_by_id)perorg_id. Stored inaudit_seals(org_id, period_yyyymm, seal_hash, row_count, first_id, last_id, sealed_at). Verification job runs weekly — chain break → operator alert. - Off-box archive shipping — weekly job streams rows older than 30 days to the off-box S3 bucket. Object Lock Compliance retention 7 years from creation.
- Boot-time grant check — application refuses to start if
audit_writerhas any ofUPDATE/DELETE/TRUNCATEprivilege onaudit_events.
Pseudonymisation key (global_pepper):
- Stored in KMS (AWS KMS or equivalent). Access controlled by operator dual-approval.
- Rotation policy: no rotation Stage 1 (loses ability to de-pseudonymise old archive entries); Stage 2 reviews dual-pepper or per-period pepper.
- Lost pepper = lost fraud-investigation path. Documented in Phase 1.7 (Security) DBA/SRE runbook.
PII classification (pii_class column):
| Class | Examples | Archival treatment |
|---|---|---|
none |
pool mode flips, public visibility preset change | preserved as-is |
personal_meta |
login success, role change (user_id + IP, no content) | actor_user_id pseudonymised, IP kept |
personal_content |
stock allocation, View-as-Agent (touches buyer PII) | actor_user_id pseudonymised; JSONB metadata may be kept depending on key registry |
sensitive |
ownership transfer with billing detail, 2FA enable/disable | actor_user_id pseudonymised; sensitive metadata keys redacted on shipping |
JSONB metadata key registry (audit_metadata_schema.ts) tags each key with pii_class so archival shipping job can redact correctly.
Alternatives Considered
- Forever in primary DB (v1 original). Simple, but violates GDPR Art. 5(1)(e) and storage cost grows unboundedly. Rejected v2.
- 12 months then hard delete. GDPR-clean, but loses audit trail for billing disputes / fraud investigations beyond 1 year and clashes with Cyprus 6-year contract limitation. Rejected.
- Indefinite archive with no pseudonymisation. Same GDPR issue as v1 original; archive immutability does not absolve controller of minimisation duty. Rejected v2.
- WORM hardware media Stage 1. Effective tamper-evidence but operationally heavy at 100-Org scale; monthly hash-chain seal + S3 Object Lock provides equivalent legal weight at lower cost. Deferred Stage 2.
- Per-period
global_pepperrotation. Strongest cryptographic posture; cost = parallel vault entries per period. Deferred Stage 2 — Stage 1 ships single global pepper.
Consequences
- 12-month active window covers full annual subscription cycle — billing disputes within the year retain full attribution.
- 7-year archive aligns with Cyprus statutory floor (contract + tax) without requiring jurisdiction-specific retention logic per Org Stage 1.
- Pseudonymisation satisfies GDPR Art. 5(1)(e) minimisation while preserving fraud-investigation path via the vault DB.
- Append-only DB role + hash-chain seal satisfy Security HIGH 3 (audit log declared immutable / tamper-evident).
- Phase 1.3 build delivers:
audit_writerrole grants, monthly seal cron,pii_classcolumn, JSONB metadata key registry. Phase 1.4.7 operator UI queries active table; vault-DB queries require dual-approval ticket. - Privacy Policy + ROPA must document the 12-month / 7-year split + pseudonymisation step (Phase 1.7 /
plans/legal-multi-party-framework.md).
Revisit trigger
- Cyprus jurisdiction pivot (e.g. ADGM): 7-year statutory floor may change — recompute archive horizon.
- WORM Stage 2 trigger: regulatory or insurance requirement explicitly mandating tamper-resistant hardware.
- Per-period pepper rotation: if pseudonymisation key compromise risk materialises.
Cross-references
- Phase 1.3 § 2.4.C (Storage + retention spec — operational implementation).
- Phase 1.3 § Architectural risks (audit log table size growth · immutability vs DR · pseudonymisation vault key custody).
- ADR 0009 (Tenancy & permission) —
audit_events.org_idscoping. - ADR 0006 (Chargeback auto-freeze) — chargeback audit events have
pii_class = sensitive. plans/legal-multi-party-framework.md(P1) — Art. 26 joint-controller framework; definescontroller_org_idinteraction with buyer PII audit events.