# Supabase security checklist for apps built with AI

By Mark Kashef, founder of Lockpick. Published 2026-10-08, updated 2026-10-08.

Keys, row level security and the Data API grant change, explained for apps built with Lovable, Bolt, v0 or by hand, with what an outside check can and cannot prove.

A Supabase app is protected by two layers. Grants decide whether a role can reach a table at all, and row level security decides which rows it can see. Your keys decide which role a request runs as. Atlas, our example app at atlas.example, uses Supabase for every example below.

## Which key can go in the browser?

Supabase says anyone can read the publishable key, so it only reaches what Row Level Security allows. A secret key bypasses every Row Level Security policy, and Supabase says never to put one in a browser, a shipped application or source control. So the publishable key in Atlas's bundle is expected, and the secret key belongs only in server code and Edge Functions.

## Is row level security on for every table?

Supabase says a table in an exposed schema without RLS is readable and writable by any role with a grant on it. Turn it on for every table your app reads through the Data API, then add a policy for each thing a person should be able to do.

## What changed about new tables?

Supabase stopped automatically exposing new tables in the `public` schema to the Data API for new projects from May 30, 2026, as a gradual rollout over a few weeks. It applies the same setting to all existing projects on October 30, 2026. After that, a new table needs an explicit grant before the Data API can see it.

## What does an outside check prove?

Less than it seems.

| What a request without signing in gets back | What it shows | What it does not show |
| --- | --- | --- |
| Rows | The role has a grant and a policy allows the read | That RLS is off, because a policy may allow it on purpose |
| An empty result | No row matched for that role | That the table is safe |
| A permission error | The role has no grant on the table | That RLS is on |

Supabase says a missing grant returns a permission error, whereas a policy that matches no rows returns an empty result. With the new default, a refused or empty read can simply mean the table was never granted to the Data API. Neither one proves the table is well protected, so review each table's grants and policies yourself.

## How to check

1. Open the Security Advisor in your Supabase dashboard and fix what it flags.
2. Run a [quick check](/check) on your published address to see what a stranger sees, then read [how we check](/docs/security/how-we-check) for what it covers.
3. Copy each fix prompt into your builder and review the change.

## Questions people ask

### Is it safe to have the Supabase publishable key in my frontend?

Yes, that is what it is for. It only reaches what your row level security policies allow, so the policies are what protect your data.

### If a table returns nothing without signing in, is it safe?

Not necessarily. An empty result or a permission error can mean the table has no grant for that role. Check the table's grants and policies directly.

## Sources

- [API keys](https://supabase.com/docs/guides/api/api-keys), Supabase docs, read on 2026-10-06
- [Breaking change, tables not exposed to Data and GraphQL API automatically](https://supabase.com/changelog/45329-breaking-change-tables-not-exposed-to-data-and-graphql-api-automatically), Supabase changelog, read on 2026-10-06
- [Row Level Security](https://supabase.com/docs/guides/database/postgres/row-level-security), Supabase docs, read on 2026-10-06
