Data
Where things live, how they’re isolated, how long they stick around, and how to get them out.
Where data lives
- Postgres — Supabase Pro. All structured data (projects, requests, form submissions, audit log).
- R2 — Cloudflare R2. File uploads, pinpoint screenshots, brand assets.
- Vault — Supabase Vault. Provider tokens, signing keys, per-agency secrets.
- Vercel — your source code and deploys. Caddi reads status; we don’t duplicate code.
All primary storage is in the United States (us-east-1).
Tenant isolation
Every row in Postgres carries a workspace_id; most also carry a client_id. Row-level security (RLS) policies enforce per-tenant scoping at the database layer — application bugs cannot leak across tenants.
An automated RLS harness runs on every PR and asserts that a sample of tables return zero rows when queried as a different agency. Merge is blocked if isolation breaks.
Encryption
- In transit — TLS 1.2+ everywhere. HSTS preload on the dashboard and portal.
- At rest, DB — Postgres on Supabase Pro (AES-256 disk + automated encrypted backups).
- At rest, files — R2 default AES-256.
- Secrets — Supabase Vault with envelope encryption and a customer-managed key.
- Form PII — encrypted with a per-agency key on write.
Retention
- Audit log — indefinite on Pro.
- Form submissions — indefinite by default. Configurable per project.
- File uploads — indefinite by default. Configurable per project.
- Postgres point-in-time recovery — 7 days.
Export
One-click full-export from Settings → Export. Produces a signed URL to a tarball with:
- JSON dumps of every table scoped to your agency.
- R2 file URLs (presigned, 7-day TTL).
- The audit log as CSV.
Deletion
Request agency deletion at Settings → Danger zone → Delete agency. We honor a 30-day grace window — your data is recoverable for that period. After 30 days everything is purged from primary storage; backups age out within 90 days.
Subprocessors
Supabase, Vercel, Cloudflare, Resend, Stripe, Sentry, Inngest. Full list with regions at /security.