Skip to content

Compliance & Audit Trail

The platform includes built-in compliance tools for automated data backups, PII (personally identifiable information) deletion, and a full audit trail. These features help meet regulatory requirements like SOC 2, GDPR, and CCPA without third-party services.

Backups, PII deletion, and audit log access are disabled by default. Cookie consent is on by default but stays invisible until a site adds a script that needs it. A Super admin enables and configures all of them in Admin > Site Config > Compliance.

Overview

There are three distinct compliance operations, all triggered by the same daily cron job:

OperationWhat It DoesOperates On
BackupExports every configured table as an encrypted JSON snapshot to cloud storage (S3/R2)Live database → cloud storage
Backup CleanupDeletes old backup files from cloud storage past their retention periodCloud storage only
PII DeletionAnonymizes or deletes records in the live database that have exceeded their retention periodLive database only

Each operation runs independently, on its own cadence, and logs every execution to the Audit table for a permanent record.

Automated Backups

When enabled, the backup system exports a full JSON snapshot of your database tables to S3-compatible cloud storage (default: Cloudflare R2). Each backup is organized by date and optionally encrypted with AES-256-GCM.

How It Works

  1. A Vercel cron job runs daily (configurable cadence).
  2. Each configured table is exported as a JSON file.
  3. If encryption is enabled, files are encrypted before upload.
  4. A manifest.json is written alongside the table files with checksums and metadata.
  5. Old backups past the retention window are automatically deleted from storage.

Storage Layout

backups/
  main/
    2026-03-01/
      manifest.json
      user.json.enc
      product.json.enc
      order.json.enc
      ...
    2026-03-02/
      ...

Configuration

SettingDefaultDescription
EnabledOffToggle backups on or off
Cadence (hours)24How often to run (24 = daily, 168 = weekly)
Retention (days)30Backup files older than this are deleted from storage
EncryptedOnAES-256-GCM encryption for backup files
Exclude Tablessite, role, apiKey, webhook, audit, ...Tables to skip (config/system tables by default)
Backup retention should be less than or equal to your shortest PII deletion retention period. Otherwise, backup files may contain personal data past its deletion deadline. The system logs a warning if this is misconfigured.

PII Deletion

PII deletion automatically removes or anonymizes personal data from your live database after a configurable retention period. This is your data retention policy enforced in code.

Strategies

StrategyWhat HappensUse When
AnonymizePII fields are replaced with [REDACTED] or null. The record itself is preserved.You need the record for reporting or accounting but don't need the personal details (e.g., shipped orders).
DeleteThe entire record is permanently removed from the database.The record has no long-term value (e.g., old contact form submissions).

Anonymize works only on tables the platform has a PII map for (orders, feedback, coupon usage, sessions, clients). A rule that asks to anonymize any other table fails the run, so it shows up in the compliance heartbeat instead of quietly scrubbing nothing. Use Delete for contacts, subscribers and carts. User accounts are not swept by PII rules; they are erased through account deletion.

Default Rules

These are the pre-configured rules (all customizable in Site Config):

TableStrategyRetentionTrigger
OrdersAnonymize30 daysAfter shipping/delivery/cancellation/refund
ContactsDelete90 daysAfter creation
SubscribersDelete90 daysInactive subscribers only
FeedbackDelete180 daysAfter creation
Coupon UsageAnonymize90 daysAfter use
Always run a Dry Run first to preview which records would be affected before running a live PII deletion.

Audit Trail

Every significant system action is logged to the Audit table, creating a permanent, tamper-evident record of what happened, when, and by whom. Super admins can browse the full audit history in Admin > Audits.

Audit Event Types

TypeDescriptionTriggered By
backupDatabase backup completed, partially completed, or failedCron job or manual trigger
backup_cleanupOld backup files were deleted from cloud storageRuns automatically after each backup
pii_deletionPII anonymization or deletion was executedCron job or manual trigger
data_exportAn admin exported table data as CSVAdmin user action
data_importData was imported in bulkAdmin user action
user_deletionA user account was deletedAdmin or user action
permission_changeA user's roles or permissions were modifiedAdmin user action
config_changeSite configuration was updatedSuper admin action

What Gets Recorded

Each audit record captures:

  • Type - The category of event (see table above).
  • Status - completed, partial, failed, or success.
  • Record count - How many records were affected.
  • Details - JSON object with specifics (tables affected, errors, duration, etc.).
  • Performed by - The user who triggered it (name, email, IP), or null for automated cron jobs.
  • Timestamp - When the event occurred.
  • Notes - Optional admin-editable notes for context.

Cookie Consent

A site with no code-injected scripts sets no cookies that need consent: the sign-in session is strictly necessary, and the built-in Vercel and Cloudflare analytics are cookieless. So no banner appears by default. Cookie Consent exists for sites that add their own scripts under Developer > Code Injection, and it is included with Code Injection at no extra cost.

Each injected script has a category. As soon as a script tagged Functional, Analytics, or Marketing is live, visitors see a banner at the bottom of every public page:

  • Reject all and Accept all are equal-weight buttons, one click each. Customize opens a preference center with one toggle per category and the vendor names and purposes you entered on each script. Nothing is pre-ticked.
  • Blocked until allowed. A script in a category the visitor hasn't allowed is left out of the page entirely — it never loads, so it can't set cookies.
  • Easy to withdraw. A Cookie preferences link in the footer reopens the preference center from any page. Withdrawing a category removes the cookies those tools left behind in the browser.
  • Asked again when things change. Adding a script, or changing a script's name, category, or purpose, asks every visitor again. Editing only the code doesn't. To ask everyone again for another reason (for example after rewording the banner), raise Policy version.
  • Choices last six months, then the visitor is asked again.

Proof of consent

Every choice writes an Audit record of type consent: a random consent ID (also stored in the visitor's browser, so one visitor's choices can be linked), the categories granted and refused, the vendor list the visitor was shown, the time, IP address, and browser, and the user if they were signed in. This is the record a regulator asks for to show consent was given.

Settings

  • Enabled (on by default) - Turning it off makes every injected script run for everyone without asking. Only do that if none of your scripts set cookies or track visitors.
  • Policy version - Raise to ask every visitor again.
  • Banner title / message - Plain, honest copy. Never "by continuing to browse you agree" — scrolling isn't consent.
Known limits. The banner relies on each script being tagged with the right category — the platform can't inspect what a snippet does. One snippet that loads several tools (such as a Google Tag Manager container) can't be split by category. The banner doesn't provide Google Consent Mode, IAB TCF vendor lists, or a US "Do Not Sell or Share" link. It shows to all visitors, not only EU/UK ones. Have the banner copy and your privacy policy reviewed before relying on them.
Scripts that existed before categories were introduced were migrated as Strictly necessary so nothing changed on upgrade. Re-tag any analytics or advertising snippet so the banner starts gating it.

Manual Controls

Super admins can manually trigger backup and PII deletion from Admin > Super Admin Tools > Compliance. Each task has two modes:

  • Dry Run - Preview what would happen without modifying any data. Shows which tables and records would be affected.
  • Live Run - Execute the operation for real. Results are logged to the audit trail.
Manual triggers bypass the cadence schedule - they run immediately regardless of when the last automated run occurred.

How They Work Together

The daily cron job runs both operations in sequence:

  1. Backup - Snapshot the current database state to cloud storage.
  2. Backup cleanup - Delete expired backup files from storage.
  3. PII deletion - Anonymize or delete old records in the live database.

This ordering is intentional: the backup captures the pre-deletion state, so you always have a recovery window. As long as your backup retention period is shorter than your PII retention period, deleted data won't linger in backup files past its deadline.

Getting Started

To enable compliance features for your deployment:

  1. Set up S3-compatible storage (e.g., Cloudflare R2) and add the connection environment variables.
  2. Generate an encryption key and add it as an environment variable.
  3. In Admin > Site Config > Compliance, toggle backup and/or PII deletion on.
  4. Use Dry Run from the Super Admin tools to verify configuration.
  5. The automated cron job handles everything from there.

For full setup details including environment variables, encryption, and disaster recovery procedures, see the project README.