Skip to main content

Handle a data export request

Two flavors:

  • Shop-wide export — the owner wants everything, ever
  • Per-customer export — a specific customer wants their record

Both flows are part of the data ownership commitment.

Drafted from planning · v0.1

The Settings → Data → Export UI button is planned; the underlying generators are not yet wired. For now the steps below describe the manual path Kvick performs on the shop owner's behalf.

Shop-wide export

Step 1: Confirm the request

The shop owner clicks Settings → Data → Export, OR contacts Kvick. Verify it's the owner (signed in as such, or external verification).

Step 2: Generate the bundle

# From a developer machine with wrangler authenticated
mkdir export-{slug}-{date}
cd export-{slug}-{date}

# Schema dump
wrangler d1 export helm-{slug}-db --output db.sql --remote

# Per-table CSVs
# (For each table — scripted as scripts/export-csvs.sh)
wrangler d1 execute helm-{slug}-db --remote --command "SELECT * FROM customers" --json > customers.json
# Then convert to CSV via a one-liner
# Or run scripts/export-csvs.sh

# R2 objects
wrangler r2 object list helm-{slug}-assets --output json > assets-manifest.json
# Bulk download (for small buckets)
# (Or use the planned scripts/export-r2.sh which uses S3-compatible API)

# Bible schema doc
cp ../../Bible/docs-site/docs/reference/database/* ./schema/
# (When schema reference is generated)

# Add README
cat > README.md <<EOF
Helm export bundle for {shop} on {date}
========================================
- db.sql: full SQLite schema + data dump
- *.csv / *.json: per-table exports for non-SQL tools
- assets/: R2 objects (receipts, photos, audit archives)
- schema/: markdown schema description per table
- restore.sh: example restore command

Restore into a fresh SQLite environment:
sqlite3 ./helm.sqlite < db.sql

Restore into a Helm-compatible D1:
wrangler d1 import {db} ./db.sql --remote
EOF

# Tar it up
tar -czf ../export-{slug}-{date}.tar.gz .

Step 3: Deliver

Upload to the shop's R2 bucket under exports/. Generate a signed URL with 7-day expiry. Email the owner.

Step 4: Audit

Write audit_events row: action='shop.data_export', executing staff = the owner (or Kvick if Kvick performed on their behalf), summary='Generated full data export bundle'.

Per-customer export

Step 1: Confirm the request

Customer profile → "Data export" button (when built; today, manually).

Step 2: Generate

The Worker assembles for that customer:

  • Profile (JSON)
  • Bikes (JSON)
  • Service tickets + lines (JSON)
  • Transactions (JSON)
  • Messages (JSON)
  • Audit log entries that reference this customer (JSON, redacted PII for non-customer rows)

Renders as:

  • A single PDF report (human-readable)
  • A single JSON file (machine-readable)

Both bundled and emailed (or a download link).

Step 3: Audit

audit_events row: action='customer.data_export', entity_id={customer_id}.

Frequency limits

The shop may request the full bundle as often as they want; it's free. The bundle generation is rate-limited to once per 24h to avoid accidental DoS (the bundle is large and slow).

The customer may request their per-customer bundle as often as they want; per-customer generation is fast.

What's in the bundle vs what's not

In:

  • All shop-owned data (customers, transactions, tickets, inventory, etc.)
  • All R2 objects (receipts, photos, archives)
  • Audit history for the shop's data
  • Schema documentation

Not in:

  • Kvick-internal logs (those are operational, not the shop's)
  • Cloudflare Workers Analytics aggregates
  • The bible (it's Kvick's; not shop-specific)
  • Other shops' anything

See also