v0.1.73 — 15 May 2026
Bug Fix — MCP-created collections now have RLS and permission policies applied
Who is affected: Anyone who used the MCP collections tool to create
collections. All such collections were created without Row Level Security,
making their data accessible to any authenticated Supabase session regardless
of the DaaS permission model.
What was wrong
The MCP tool executed CREATE TABLE but skipped the two post-creation steps
that the UI has always performed:
change_table_owner— transfers table ownership so theSECURITY DEFINERfunction can create policies.apply_rls_to_collection— enables RLS and creates the full set of DaaS access policies (admin bypass, permission-based CRUD, and optional self-access for tables with auser_idcolumn).
Additionally, the resource_uri performance index was not created, degrading
query speed on every scoped API call for affected collections.
What changed
The MCP tool now performs all three steps immediately after CREATE TABLE,
matching the UI behaviour exactly. New collections created via MCP are
indistinguishable from those created via the UI with respect to RLS.
Upgrading — existing collections
A backfill migration is included:
supabase/migrations/20260508000001_backfill_rls_mcp_collections.sql
It automatically applies RLS and the standard policies to every collection
registered in daas_collections that currently has RLS disabled. Run it via
supabase db push (or your normal migration pipeline).
Action required
If you have collections created via MCP, run the backfill migration before your next production deployment to close the permission gap.
supabase db pushBefore / After
| Property | Before this fix | After this fix |
|---|---|---|
| RLS enabled on MCP-created table | ❌ Off | ✅ On |
| Admin bypass policy | ❌ Missing | ✅ Created |
| Permission-based policies (read/create/update/delete) | ❌ Missing | ✅ Created |
Self-access policies (tables with user_id) | ❌ Missing | ✅ Created |
resource_uri index | ❌ Missing | ✅ Created |
AWS RDS environments
On RDS there is no true superuser, so the change_table_owner step may log a
warning: must be able to SET ROLE "postgres". This is harmless —
apply_rls_to_collection is independently SECURITY DEFINER and has
sufficient privileges to enable RLS and create policies on its own.
Bug Fix — services.custom(name, { elevated: true }) now correctly elevates permissions in nested services
Who is affected: Anyone who calls services.custom('name', { elevated: true }) from a
runtime extension or cron job, or anyone who experienced Custom service "name" not found or inactive errors from extensions in production.
What was wrong
Issue 1 — opts ignored: Passing { elevated: true } as the second argument to
services.custom() had no effect. The nested custom service always ran with the calling user’s
original permissions, so restricted writes inside the service were still blocked.
Issue 2 — production cache miss: In production Next.js builds, calling services.custom()
from an extension could throw Custom service "name" not found or inactive even when the
service was active. This happened because Next.js evaluates module singletons in separate
chunk contexts — after a service was updated via the API, the extension runtime’s cache was
empty and the lookup failed.
What changed
{ elevated: true } is now properly forwarded into the nested service’s accountability,
bypassing its permission checks while the calling user is still recorded in the audit trail
(user_created/user_updated and daas_activity).
The cache-miss issue is resolved with a database fallback: on a cache miss, the runtime now
queries daas_custom_services directly and compiles the service on demand.
Before / After
| Scenario | Before this fix | After this fix |
|---|---|---|
services.custom('svc', { elevated: true }) from extension | ❌ opts ignored — policies still enforced | ✅ admin acc propagated — permission checks bypassed |
services.custom() from extension after service update (production) | ❌ May throw “not found or inactive” | ✅ Falls back to DB lookup and compiles on demand |
Triggering user in user_created / daas_activity when elevated | ✅ Preserved | ✅ Still preserved |