Why every brain table should have row level security on, and how to exempt one on purpose.
Row level security (RLS) is a Postgres feature that blocks every read and write on a table unless a policy allows it. Supabase publishes the tables in your public schema through an automatic web API, and anyone holding the project's public anon key can call that API. A table without RLS can be read and changed by anyone with that key.
The brain therefore expects RLS on every table in the public schema, including tables it did not create. This guide shows how to find tables without it, how to turn it on, and how to exempt a table on purpose.
nexusbrain init --supabase or nexusbrain init --url.psql or the Supabase SQL editor, for the exemption steps.BYPASSRLS privilege (such as postgres) for applying the fixes.The brain is meant to connect as a role with the BYPASSRLS privilege, so turning RLS on does not block the brain itself. What it blocks is the public web API: with RLS on and no policies, the anon key gets nothing.
On the local engine (PGLite) there is no web API and only one user, so the check is skipped and reports [OK] rls: Skipped.
nexusbrain doctor checks every base table in the public schema, not a fixed list.auto_rls_on_create_table, that turns RLS on for every new table created in public.Do not add --fast, which skips all database checks.
nexusbrain doctorYou should seeOn a protected brain: [OK] rls: RLS enabled on 42/42 public tables, with your own counts.
Gaps show as [FAIL] rls: 2 table(s) WITHOUT Row Level Security: my_table, other_table. followed by Fix: and a ready-to-run ALTER TABLE statement for each table.
Any failed check makes nexusbrain doctor exit with code 1, so a CI job or script that runs it will stop.
You should seeYou have the list of unprotected tables.
If the gaps are brain tables on an older install, applying pending migrations fixes them. For any table, you can also run the statements doctor printed.
Add --dry-run first if you want to see the plan without changing anything.
nexusbrain apply-migrations --yesYou should seeThe migrations run. Existing public tables without RLS and without an exemption comment get RLS turned on.
Copy the ALTER TABLE lines from the Fix: part of the doctor message and run them as a role with BYPASSRLS.
ALTER TABLE "public"."my_table" ENABLE ROW LEVEL SECURITY;You should seePostgres answers ALTER TABLE.
nexusbrain doctorYou should see[OK] rls: RLS enabled on all public tables.
With RLS on and no policies, the anon key can no longer read the table. If another app reads that table through the Supabase API, give it a policy, or exempt the table as shown below.
Some tables are meant to be public, such as analytics a dashboard reads with the anon key. You can mark such a table as exempt with a Postgres comment. There is deliberately no brain command for this: it has to be written in SQL, where it is visible in schema diffs and survives pg_dump.
The comment must start with NEXUS_BRAIN:RLS_EXEMPT, then a space, then reason= and a reason of at least 4 characters that does not start with a space.
COMMENT ON TABLE public.my_table IS 'NEXUS_BRAIN:RLS_EXEMPT reason=public dashboard reads this with the anon key';You should seePostgres answers COMMENT.
The exemption only matters for a table whose RLS is off. New tables get RLS on automatically, so you usually need this step.
ALTER TABLE public.my_table DISABLE ROW LEVEL SECURITY;You should seePostgres answers ALTER TABLE.
nexusbrain doctorYou should seeThe rls check is [OK] and ends with (1 explicitly exempt: my_table). Doctor lists exempt tables on every run so they never go unnoticed.
Add exemption comments before you upgrade or run nexusbrain apply-migrations. The migration backfill turns RLS on for every public table that does not carry a valid comment, including tables you left open on purpose.
COMMENT ON TABLE public.my_table IS NULL;You should seePostgres answers COMMENT.
ALTER TABLE public.my_table ENABLE ROW LEVEL SECURITY;You should seenexusbrain doctor counts the table as protected and no longer lists it as exempt.
NEXUS_BRAIN:RLS_EXEMPT comment.The comment must include reason= followed by at least 4 characters. A bare NEXUS_BRAIN:RLS_EXEMPT does not count.
The comment must be at the very start, with no text before it.
does not have BYPASSRLS privilege or does not have BYPASSRLS.The brain is connected as a role that cannot safely change RLS. Re-run nexusbrain apply-migrations --yes with a connection that uses postgres or another role with BYPASSRLS.
[WARN] rls: Could not check RLS statusDoctor could not read the table list. Check that the database is reachable and that the brain role can read pg_tables, then run nexusbrain doctor again.
The migration turned RLS on for that table. Add a Postgres policy for the access the app needs, or exempt the table with a comment and turn RLS off for it.