Appearance
Database & RLS
PostgreSQL with Row-Level Security on every user-facing table. 58 tables, 118 RLS policies — verified identical between Dev and Prod at baseline. The schema is captured as a one-time squash migration: supabase/migrations/20260710134136_baseline_schema_from_prod.sql.
profilesis the identity hub — one row per auth user, referenced across all three pillars.
Entity map (grouped by pillar)
Table groups
| Group | Tables (representative) |
|---|---|
| Identity & platform | profiles, profile_items, profile_revisions, profile_analytics, platform_features, domain_features, business_domains, user_domains, reserved_slugs |
| Subscriptions | subscription_tiers, user_subscriptions, subscription_usage_logs, user_feature_permissions |
| Digital Menu | digital_menus, digital_menu_categories, digital_menu_items, digital_menu_item_variants, digital_menu_item_images, digital_menu_item_favorites, digital_menu_item_revisions, digital_menu_settings, digital_menu_qr_codes, digital_menu_qr_scan_analytics, digital_menu_analytics, digital_menu_cash_counts |
| BioLink | bio_pages, bio_links |
| Studio / My Setu | studio_websites, studio_pages, studio_blocks_library, studio_analytics_page_views, setu_pages, setu_blocks, templates, template_selections |
| Engagements / Feedback | engagement_forms, engagement_responses, engagement_response_contacts |
| QR & reminders | qr_codes, reminders, reminder_categories |
| vCard / business card | vcard_customizations, service_customizations |
| public_page_ops (Type B ops) | public_page_ops_cache_master, *_cache_log, *_cache_operations, *_cron_jobs, *_cron_executions, *_edge_function_logs, *_cleanup_log, *_housekeeping_config (+ dated partitions) |
See documentation/02-technical/database-schema-snapshot.md for the full column-level snapshot.
RLS model
- RLS on every user-facing table — defense-in-depth. The Edge Function is the primary write-enforcement layer; both are always required.
- Append-only tables carry no UPDATE/DELETE policy (e.g. analytics / log tables).
- Public pages read through
get_public_*RPCs (SECURITY DEFINER) that projectrow_to_jsonso the anon role never touches base tables directly.
DB rules [ENFORCED]
- Migrations: 14-digit
YYYYMMDDHHMMSS_name.sql(prefernpx supabase migration new; never the oldNNN_form — it collides on the version parser). - Never edit a merged migration — add a new one.
- Expand-contract always: add-nullable → backfill → switch → drop. Never a breaking change in one step.
- Use
row_to_json(table.*)in RPCs for forward-compat. - Capture cron (
pg_cron) schedules and storage buckets as migrations, not just live DB state.
[TRANSITIONAL] — not yet replicated to Dev
- The pg_cron job hardcodes Prod's own Edge Function URL — needs a project-URL-aware rewrite before Dev gets one.
- The
profile-picturesstorage bucket — environment-specific data, not portable schema.
See Migrations guide and Supabase integration.