Creating a Supabase project
Create an account
Go to supabase.com and create a free account if you do not have one.
Create a new project
From the Supabase dashboard click New project. Choose your organisation, give the project a name (e.g.
sintesis-prod), and select a region close to your users. Set a strong database password and save it somewhere safe — you will need it for direct DB connections.Copy your project credentials
Once the project is provisioned, go to Project Settings → API and copy:
- Project URL →
NEXT_PUBLIC_SUPABASE_URL anonpublic key →NEXT_PUBLIC_SUPABASE_ANON_KEYservice_rolesecret key →SUPABASE_SERVICE_ROLE_KEY
.env.local (or Vercel environment variables for production).Running migrations
Sintesis maintains a versioned migration history insupabase/migrations/. Every file is prefixed with a four-digit sequence number and must be applied in order.
Apply locally
The local Supabase stack applies all migrations automatically when you runpnpm supabase:start (via supabase db reset under the hood). To replay migrations against an already-running local stack:
Push to a remote project
Link to your remote project
Replace You will be prompted for the database password you set when creating the project.
<project-ref> with the reference shown in your Supabase dashboard URL (e.g. abcdefghijklmnop).Migration overview
The table below summarises the major migration milestones in sequence:| Range | What is set up |
|---|---|
0001 | Base schema: tenants, profiles, memberships, instruments, RLS policies, is_member_of() helper |
0002–0006 | Dev RLS adjustments, roles & permissions seed, profiles trigger, membership admin reads, onboarding flow |
0007–0010 | Membership self-join, obras table (v1 and v2), obras finish configuration |
0011–0013 | Certificates table, notifications table, materials tables |
0014 | Storage bucket obra-documents with RLS policies |
0015–0017 | Material orders doc reference, obra pendientes, certificates extras |
0018 | Realtime publication for notifications table |
0019–0024 | Pendientes notifications link, due time, APS models, flujo actions, notification types |
0027 | Superadmin user, is_superadmin() helper, automatic superadmin-to-tenant trigger |
0031 | Invitations table |
0038–0039 | RLS policy optimisation, helper function security hardening |
0040–0041 | Calendar events, obra–calendar link |
0044 | Audit log |
0045 | Tenant API secrets |
0048–0054 | Obra tablas, defaults, OCR templates, flujo execution tracking, workflow notifications, macro tables |
0060 | Full permissions system with role templates and macro-table-level permissions |
0062–0070 | Sidebar macro tables, subscription plans, tenant usage enforcement, usage event logs, quick actions |
0071–0086 | Report presets, background jobs, obra reporting, signal logs, custom data, document uploads, audit log refinements |
Row Level Security (RLS)
Every table in Sintesis has RLS enabled. The security model is built on three helper functions defined in0001_base_schema.sql and progressively hardened in later migrations:
is_member_of(tenant uuid) → boolean
is_member_of(tenant uuid) → boolean
Returns
true if the currently authenticated user (auth.uid()) has a row in public.memberships for the given tenant. Superadmins always return true (added in migration 0027).Used on virtually every tenant-scoped table to gate SELECT, INSERT, UPDATE, and DELETE.is_admin_of(tenant uuid) → boolean
is_admin_of(tenant uuid) → boolean
Returns
true if the user is a superadmin or has role = 'owner' or role = 'admin' in memberships for the tenant.Used to protect administrative operations such as managing other users, editing roles, and updating tenant settings.has_permission(tenant uuid, perm_key text) → boolean
has_permission(tenant uuid, perm_key text) → boolean
Evaluates the full permission chain:
- Superadmin → always
true. is_admin_of(tenant)→true.- Direct
user_permission_overridesgrant forperm_key. - Any
user_roles→roles→role_permissions→permissionspath for the tenant.
is_superadmin() → boolean
is_superadmin() → boolean
Reads
profiles.is_superadmin for the current user. Superadmins bypass all tenant scoping and are automatically added as owner to every tenant via a database trigger.All helper functions are
SECURITY DEFINER or STABLE SQL functions to avoid RLS recursion. Migration 0038 and 0039 contain specific optimisations to prevent stack overflow when policies call each other.Storage — obra documents
Migration0014_storage_obra_documents.sql creates the obra-documents storage bucket and attaches four RLS policies to storage.objects:
| Policy | Operation | Condition |
|---|---|---|
obra-documents read | SELECT | auth.role() = 'authenticated' |
obra-documents insert | INSERT | auth.role() = 'authenticated' |
obra-documents update | UPDATE | auth.role() = 'authenticated' |
obra-documents delete | DELETE | auth.role() = 'authenticated' |
public = false). Files are served via signed URLs generated on the server.
To create the bucket manually (e.g. if you are using a hosted project without running migrations via CLI):
0014_storage_obra_documents.sql in the Supabase SQL editor.
Realtime — notifications
Migration0018_notifications_realtime.sql adds the public.notifications table to the supabase_realtime publication:
INSERT events on the notifications table using the Supabase Realtime client:
Seed data
supabase/seed.sql is executed automatically after supabase db reset and after supabase start finishes resetting the local database. It inserts:
- A default tenant with ID
00000000-0000-0000-0000-000000000001and nameDefault Tenant. - Three sample instruments (
Guitar,Piano,Drums) scoped to the default tenant.
owner:
