The Database Setting Most AI App Builders Never Turn On
Quick answer: if you built your app with an AI tool like Lovable, Bolt, or a similar builder, your frontend almost certainly talks to its database through a public key that's sitting right there…
Quick answer: if you built your app with an AI tool like Lovable, Bolt, or a similar builder, your frontend almost certainly talks to its database through a public key that's sitting right there in your site's JavaScript, visible to anyone who looks. That's intentional, not a mistake. What actually keeps your data private is a database-level setting called Row-Level Security (RLS), and it's the thing that's easy to leave off, or leave too permissive, while you're busy getting the app to work. Through 2026, independent security researchers have repeatedly found this same pattern across AI-built apps: the public key works exactly as designed, and the database behind it has no real restriction on what that key can read or write. You can check your own app for this in about ten minutes.
This isn't a flaw unique to one builder or one platform. It's a consequence of how these tools are designed to work: your database needs to be reachable directly from the browser for the app to function at all, so the key that lets it do that is meant to be public. The part that's supposed to carry the actual security weight is a setting most people never think to look for.
In short: a public API key in your app's JavaScript isn't the problem, it's supposed to be there. The problem is when the database behind it has Row-Level Security turned off, or turned on with a rule broad enough to let anyone through anyway.
How AI App Builders Talk to Your Database
Most AI app builders don't make you set up a backend by hand. Under the hood, many of them connect your generated frontend to a hosted Postgres database, often through a backend-as-a-service layer like Supabase, that automatically exposes your tables as a web API. Your app's JavaScript then talks to that API directly using a publishable key, often called an "anon" or public key, baked into the code that ships to every visitor's browser.
That key being visible isn't a leak. It's how the architecture is meant to work, the same way a storefront's address isn't a secret. What determines whether that's safe is what happens on the other side of that key: whether the database itself checks who's asking before handing back rows.
The Setting That's Supposed to Do the Actual Locking
Row-Level Security is a feature built into Postgres (and used the same way by most tools built on top of it) that decides, at the database level, which rows a given request is allowed to see or change. Done correctly, it means a request using the public key can only read or write the rows that belong to whoever is making that request, not the whole table.
There are two ways this ends up not working:
RLS was never turned on for a table. Without it, a request using the public key can read or write every row, not just the requester's own data, subject only to whatever broader permissions the database role has.
RLS is turned on, but the rule attached to it is too broad. A policy written to just let any request through, often added during prototyping to stop getting blocked while building, has the same end result as having no restriction at all.
Either way, the outcome is the same: a request made with your app's public key, the same key sitting in your JavaScript, can reach data it shouldn't.
Why This Is Easy to Miss When You're Building Fast
Testing your own app rarely surfaces this, because you're testing it the way it's meant to be used: logged in as yourself, looking at what your account is supposed to see. The app's screens only ever ask for your own data, so everything looks correct. The gap only shows up when a request goes straight to the API instead of through your app's interface, which is exactly what your own testing never does.
It's also an easy step to skip for a structural reason: a table with RLS off behaves identically, from inside your app, to a table with RLS correctly configured. Nothing breaks, nothing looks different, and nothing prompts you to go check. The setting matters entirely to someone who isn't using your app the normal way, which is precisely the person it's meant to stop.
How to Check Your Own App
| Step | What you're doing | What to look for |
|---|---|---|
| Find your project's API URL and public key | Usually visible in your builder's project settings, or directly inside your site's deployed JavaScript bundle | This is expected to be visible. You're not looking for a leak here, just gathering what you need for the next step. |
| Request a table directly, without logging in | Using curl, request one of your data tables through the API with the public key attached, as an anonymous, logged-out request | Do you get back more than you should, rows belonging to other users, or any data at all on a table that should be private? |
| Check your backend dashboard | Most platforms built on Postgres show an explicit setting or warning for whether Row-Level Security is enabled per table | A table with RLS off, or a policy that reads as "allow everything," is the thing to fix |
| Test as a second account | Create a second test user and confirm it only ever gets its own data back, not just that your main account's data looks right | This catches policies that exist but don't actually scope correctly to the requesting user |
If the direct request in step two returns data you wouldn't expect a stranger to see, that's the finding, and it's worth fixing before you treat the app as launch-ready.
What to Actually Do About It
Turn on Row-Level Security for every table that isn't meant to be fully public. Turning it on isn't the whole fix by itself, since a policy that allows everything produces the same exposure as no policy at all, so the actual fix is writing rules that scope access to the requesting user specifically, a rule that reads roughly as "a user can only read or write rows that belong to them," rather than a catch-all written to stop development errors from blocking you while you build.
Don't try to fix this by hiding or rotating the public key instead. That key is meant to be visible, and treating it as the thing to protect misses where the actual boundary is supposed to sit. A security check that looks at how your app's public endpoints actually behave is a useful second opinion here, particularly since this is the kind of gap that a working, good-looking app gives you no other signal about.
Frequently asked questions
Final Thoughts
The uncomfortable part of this isn't that the key is public, it's supposed to be. It's that a working app and a dangerously open one can look exactly the same from inside the product you built, which means the usual way you'd catch a problem, using the thing and seeing if it works, doesn't apply here.
The check itself doesn't take long, and it doesn't require understanding the rest of your app's architecture. It requires asking one specific question about one specific setting, and it's worth asking before real users, and their real data, are the ones who find out the answer first.
If you built your app quickly with an AI tool and haven't specifically checked this, it's worth ten minutes before you consider the app done.
How FlawPilot helps
FlawPilot finds security and quality issues in your AI-built app and shows you how to fix them. It checks your deployed site across security, performance, infrastructure, and SEO, and scans your source code for vulnerabilities, hardcoded secrets, and vulnerable dependencies.
Every finding is prioritized and explained in plain English, with the actual fix: the configuration change, DNS record, security header, or code change needed. For supported findings, AI-powered guidance adds step-by-step instructions and suggested code fixes.
Connect your Git provider to scan your repository alongside your live site, so application findings, code vulnerabilities, secrets, and dependency issues all land in one place.
It fits your existing workflow too: a REST API for scores and findings, an embeddable security badge, and an MCP server so tools like Claude, Cursor, or ChatGPT can read your findings and help you work through them.
The boundaries are clear: the public website scan reads only publicly accessible signals, with no agent or credentials required, and source-code scanning is opt-in and read-only. Fixes are never applied or merged without your review.
Verify your AI-generated app is production-ready.
117 security checks in 60 seconds - free, no account needed.
Scan one page
Enter a URL - no account, no install.
Run a Site Health check
Requires a free accountCrawls every page we can reach and scores each one, so a slow template deep in the site stops hiding behind a healthy homepage.
Scan your source code
Requires a free accountConnect a Git provider to check for vulnerabilities, secrets, and risky dependencies.