Bolt and Supabase security

Bolt wires your app to Supabase and ships a public key so the frontend can read and write data. That key is fine on its own. It becomes a problem when the tables behind it have no access rules, because then anyone (not just your users) can read everything. This page checks it on your live app.

Scan your Bolt + Supabase app

The scan reads only what your app already ships to every visitor. It never stores your row data.

What it means

Your database tables can be read by anyone, not just logged-in users. Someone could open your app's data — customer emails, orders, private records — without ever creating an account.

Why AI tools cause it

Supabase only protects a table once you turn on Row Level Security (RLS) and write access rules for it. AI tools frequently build a working app without doing that, so the tables are wide open by default.

How xlogs checks it

xlogs finds your public Supabase URL and anon key in your app (the same ones your frontend uses), asks the database as an anonymous user which tables it can read, and flags any that return real rows. Read-only: it never writes or deletes.

How the Bolt and Supabase combination goes wrong

This is not a bug in either tool. Row Level Security is a setting you turn on per table, and a fast build skips it. Once it is skipped, the public key Bolt ships becomes a key to your whole database.

The scan finds your public Supabase URL and key in your app, then asks the database, as an anonymous user, whether it returns rows it should not. It reports the table and its columns as proof and never stores your row data.

How to fix it

The goal: Row Level Security is enabled on every table, with policies so a table only returns rows to users allowed to see them; no table returns private data to an anonymous request.

  1. Enable Row Level Security (RLS) on every table, especially the affected spot
  2. Add a policy per table so only authenticated users can read/write their own rows (keep intentionally-public tables public on purpose)
  3. Re-check that an anonymous request no longer returns private rows
Paste this to your AI coding tool (Lovable, Cursor, Claude Code):
In my app (the spot the scan shows): anyone can read this database table. Please make this true: Row Level Security is enabled on every table, with policies so a table only returns rows to users allowed to see them; no table returns private data to an anonymous request. Steps: Enable Row Level Security (RLS) on every table, especially the spot the scan shows; Add a policy per table so only authenticated users can read/write their own rows (keep intentionally-public tables public on purpose); Re-check that an anonymous request no longer returns private rows. Then tell me exactly what you changed, and do not print any secret values back to me.

Then verify: After you deploy the policies, xlogs repeats the same anonymous read test and confirms the table no longer returns data without a login.

Common questions

Is it safe to expose the Supabase anon key in a Bolt app?

Yes, the anon key is meant to be public and ship in the browser. It is safe only when your tables have Row Level Security policies. Without them, that public key becomes a key to your whole database. The scan tells you which case you are in.

How do I check Row Level Security on my Lovable project?

Test it from the outside, the way anyone on the internet would. The scanner sends an anonymous read to your tables. If real rows come back, RLS is not protecting that table. If nothing comes back, it is doing its job.