Skip to main content

Storage limitation, made enforceable.

GDPR Art. 5(1)(e) says personal data must be kept "no longer than is necessary." Below is the live retention table, sourced from src/lib/server/config.ts, and the timestamps of the cron jobs that enforce it. If the schedule slips, this page is the proof.

Per-table retention

Live values
TableWindowPolicyWhy this number
analytics.events60 days (~2 months)Hard DELETE on daily cronIndividual page views, journey transitions. Past this window, only the daily aggregate row survives.
analytics.sessions60 days (~2 months)Hard DELETE on daily cronPer-visit metadata: started_at, last_seen, consent_tier. Same retention as events to keep the join clean.
analytics.daily_page_stats365 days (~1 years)Hard DELETE on daily cronPre-aggregated rollups (path / day / count). No individual visitor data — kept longer to draw long-term traffic trends.
analytics.consent_events395 days (~1.1 years)Hard DELETE on daily cronProof of consent (or its withdrawal). 13 months satisfies Art. 7(1) demonstrability without overshooting.
feedback.feedbackManual deletion onlyUser-authored content. We have no automatic expiry; you can request removal at any time and the operator deletes the row.
auth.sessionBetter Auth session expirySessions expire on their own per Better Auth config. Logout invalidates immediately.

Cron status

Live

Loading…

How a row dies

  1. Daily at 02:00 UTC Vercel Cron pings /api/cron/analytics-cleanup with a Bearer token.
  2. The job computes today's cutoffs from ANALYTICS_RETENTION_DAYS and CONSENT_RETENTION_DAYS.
  3. A single DELETE per table removes everything older than the cutoff. No soft delete, no archival bucket — the row is gone.
  4. The run is recorded in jobs.job_executions with rows-deleted, status, duration. That table is what this page reads.
  5. If the schedule slips beyond 26 hours, the badges above flip to "overdue" and the /admin/analytics tile flags it too.