Guide: From Prototype to Production
Built with Lovable, Bolt or Replit: what is likely still open
Lovable, Bolt and Replit turn an idea into a running web application in a short amount of time, and for prototypes, vibe coding with these tools is a genuine win. Once real customer data and roles enter the picture, though, a handful of technical mechanisms decide whether the application holds up. These come with check steps that can be run directly, without outside help.

Short answer
Before putting a Lovable, Bolt or Replit app into production, six points are usually still open: row-level security in the database, secret keys in the frontend, missing migrations, access rights enforced only in the interface, logs containing personal data, and the portability of code and data. All six can be checked before launch.
01
What do Lovable, Bolt and Replit actually deliver, and where does that stop?
These tools turn natural-language descriptions into working web applications and take care of setup, hosting and often the database too. For prototypes and internal experiments, that is a genuine win. What stays open is whatever no prompt ever asked for: access rules, key management, schema versioning, logging and a way out of the platform.
According to its documentation, Lovable generates standard projects with Vite and React and relies on Supabase in the backend for the database, login, storage and server-side functions. Bolt projects can be downloaded as a ZIP, and Replit is a browser-based development environment. None of these points is a flaw unique to these tools: the same gaps appear in hand-written code too, just more slowly.
A preview answers whether the idea works, not whether the application can handle real customer data, multiple roles and access from outside. From prototype to a production-ready application covers the extra questions a production system raises. Covered here are the mechanisms worth checking first.
- 01PromptFunction gets described
- 02PreviewApplication runs visibly
- 03PublishPublic address
- 04Real usersReal data, real access
- 05OperationsChanges, updates, handover
02
What is row-level security, and why does the default matter so much?
Row-level security (RLS) is a PostgreSQL feature that decides, row by row, who can read or change which data. When browser code talks directly to a Supabase database, RLS is the actual access control. According to Supabase, if RLS is missing on a table in the public schema, anyone with the project address can read, change and delete there.
Many generated applications have no server of their own checking requests: the browser calls Supabase's database API directly and sends along a public key. Supabase calls this the Publishable Key (formerly anon) safe to expose in the frontend, but only because the database itself decides which rows the anon and authenticated roles can see. Grants govern whether a role may perform an operation at all, policies govern which rows that applies to.
The default depends on how the table was created
Tables created through the Supabase dashboard have RLS active by default. Tables created in the SQL editor or by other tools only get RLS once someone switches it on, and that is exactly how AI assistants work when they run schema changes as SQL. RLS with no policy returns no data at all through the API: that looks like a bug, but it is actually the safe state.
-- Enable RLS for the table
alter table public.orders enable row level security;
-- Authenticated users read only their own orders
create policy "Read own orders"
on public.orders for select to authenticated
using ( (select auth.uid()) = user_id );
-- New rows can only be created in the user's own name
create policy "Create own orders"
on public.orders for insert to authenticated
with check ( (select auth.uid()) = user_id );03
How can RLS be checked directly in a project?
The fastest route is the Security Advisor in the Supabase dashboard, which flags tables with no RLS and policies that are too permissive. Two SQL queries and an outside test help after that: if rows come back using the public key with no login that an outsider should never see, the table is exposed.
Run the Security Advisor
From the dashboard or with
supabase db advisors. The main ones to watch arerls_disabled_in_public,policy_exists_rls_disabledandpermissive_rls_policy.Query the system catalogues
The SQL queries here show RLS status and policies. Every table with
rowsecurity = falseneeds a justification, and every policy needs a business rule behind it.Test from outside
Query a table with the public key and no login, only against an organisation's own project.
[]means either RLS is working or the table is empty.Cross-test with two accounts
Try to read or change someone else's records using two test accounts. Only this shows whether the policies actually reflect the business rule.
-- Which tables in the public schema have no RLS?
select schemaname, tablename, rowsecurity
from pg_tables
where schemaname = 'public'
order by rowsecurity, tablename;
-- Which policies exist, for which roles and operations?
select tablename, policyname, cmd, roles, qual, with_check
from pg_policies
where schemaname = 'public'
order by tablename, policyname;# Run only against an organisation's own project
curl 'https://<PROJECT_REF>.supabase.co/rest/v1/<table>?select=*' \
-H "apikey: <PUBLISHABLE_KEY>"Checklist
RLS checklist before launch
Tick off what can be demonstrated.
04
What happened in CVE-2025-48757?
The NVD entry describes insufficient row-level security policies in Lovable up to 15 April 2025, which let unauthenticated remote attackers read and write arbitrary database tables of generated sites. The entry is rated critical under CVSS 3.1, and the vendor disputes the vulnerability.
According to the disclosure, the generated frontends called the database API directly with the public anon key and relied on RLS alone. Where policies were missing or too broad, a modified request from the browser was enough. An automated scan on 21 March 2025 examined 1,645 Lovable projects this way, and 170 of them returned data (disclosure statement, 2025). Names, email addresses, API keys for third-party services and payment status are all named as exposed.
Discovery
The missing policies were found, according to the disclosure.
Reported to Lovable
The scan across 1,645 projects ran the same day.
Cut-off date in the CVE entry
Lovable is named as affected up to this date.
Disclosure
The details become public.
CVE published
Entry recorded in the CVE Program and the NVD.
Today
What belongs in a fair reading of this
- The CVE entry notes that Lovable disputes the vulnerability: responsibility for protecting application data, it says, lies with customers.
- The disclosure states it came from an employee of Replit, a vendor in the same segment. That does not make the numbers wrong, but it belongs in the context.
- Lovable now runs a basic scan with RLS linting on publish, and a deeper scan is available on request. Its own documentation says these tools cannot guarantee complete security.
05
Are secret keys sitting in the frontend?
Anything that reaches the browser can be read by any visitor, even inside minified JavaScript. Public keys such as Supabase's Publishable Key are meant for exactly that. Secret keys for the database, for language models, or for payment or mail services, on the other hand, belong exclusively in server-side functions with a stored secret.
A Supabase Secret Key (sb_secret_..., formerly service_role) bypasses every RLS policy, and Supabase now rejects it in the browser with HTTP 401. Keys for other services have no such protection. The typical route into the frontend is environment variables: in Vite projects, Lovable included, anything with the VITE_ prefix ends up in the shipped code. A VITE_OPENAI_API_KEY is therefore public information.
- Search the loaded scripts under 'Sources' in the browser's developer tools for
sb_secret_,service_role,sk-andBearer. - Search the repository and its history with the commands shown here: a key that was ever committed stays readable in old commits.
- Check the 'Network' tab for which services the browser calls directly using a secret key.
# Current state of the repository
git grep -n -I -E "sb_secret_|service_role|sk-[A-Za-z0-9]{10,}|VITE_[A-Z0-9_]*(SECRET|TOKEN|PRIVATE)"
# Full history of every branch, deleted files included
git log --all -p | grep -n -E "sb_secret_|service_role|sk-[A-Za-z0-9]{10,}"06
Why are migrations often missing, and how can that be spotted?
Migrations are versioned SQL files that record every schema change. When the schema gets changed directly in the dashboard or by a tool, the current state only exists in the running database: no identical test environment, no clean way to roll back. Whether the supabase/migrations folder exists and fully describes the database is the way to check.
Supabase recommends running every schema change, even small ones, through migration files rather than directly through the dashboard or the SQL editor, and it records applied migrations in supabase_migrations.schema_migrations. In prototypes, the opposite has often happened: tables created in conversation with an assistant, columns added through the dashboard, a policy loosened for troubleshooting.
supabase link --project-ref <PROJECT_REF>
supabase db pull # save today's schema as the baseline migration
supabase db diff -f dashboard_changes # capture changes made in the dashboard
supabase migration new rls_orders # create a new, empty migration file
supabase db reset # apply every migration locally from scratch
supabase db push # apply the migrations to the remote projectSelf-test: does supabase db reset build a schema locally from the migrations that matches production? If not, the history is incomplete. Recognising technical debt in AI-generated code describes the long-term consequences.
07
Is access control implemented only in the interface?
The question is whether the code merely hides buttons and pages while never checking the role where the data actually lives. Anyone who rebuilds the request in the browser bypasses the interface entirely. Access control only becomes effective once the database or server enforces it on every request, no matter where that request comes from.
Where does the access decision get made?
| Criterion | Only in the interface | In the database or server |
|---|---|---|
| Effect | A button gets hidden | The request gets rejected |
| Direct API call | still works | gets checked all the same |
| The role lives in | metadata a user can edit | its own table with no write access |
With Supabase, policies based on user_metadata can be bypassed because users can edit that metadata themselves; the advisor flags this as rls_references_user_metadata. Roles belong in a table of their own.
create table public.roles (
user_id uuid primary key references auth.users (id) on delete cascade,
role text not null check (role in ('admin', 'staff'))
);
alter table public.roles enable row level security;
-- Users read only their own role; with no write policy, nobody can grant roles via the API
create policy "Read own role"
on public.roles for select to authenticated
using ( (select auth.uid()) = user_id );
-- Only administrators see invoices
create policy "Admins read invoices"
on public.invoices for select to authenticated
using (
exists (
select 1 from public.roles r
where r.user_id = (select auth.uid())
and r.role = 'admin'
)
);Self-test: using an account with no special rights, copy a data request from the 'Network' tab and change the identifier, for example id=eq.124 instead of id=eq.123, or query a table meant to be visible only to administrators. If data comes back, the access control is only for show.
08
What ends up in the logs?
While building, output such as console.log(user) helps with debugging. Once published, it ends up in every visitor's browser console or in the platform's function logs, email addresses, tokens and form contents included. Logs are meant to make events traceable, not to collect personal data, and that can be checked before launch.
Everyone can see browser output, and it reveals table names that make an attack easier. Server logs can end up readable by more people than the actual data itself, and they follow their own retention rules.
What belongs in a log
| Criterion | Does not belong there | Belongs there |
|---|---|---|
| Identity | Names, email addresses | A pseudonymous identifier |
| Access | Passwords, tokens, API keys | An event, such as a failed login |
| Content | Full requests, free-text form fields, prompts containing personal data | Result, error code, request identifier |
git grep -n -E "console\.(log|debug|info)\(" -- src supabase/functionsGDPR-compliant AI covers the data protection side of this.
09
Can code and data actually be taken elsewhere?
For code, yes: Lovable syncs with GitHub, and Bolt and Replit both offer a ZIP download. Data, secrets and platform services such as login, file storage and server-side functions are harder. Testing the move while nothing is urgent, rather than only once pricing, terms or requirements change, is the safer order.
Export options per vendor documentation
| Criterion | Lovable | Bolt | Replit |
|---|---|---|---|
| Code | Two-way GitHub sync; download available on paid plans | Export as a ZIP | Download as a ZIP; Git integration |
| Database | Supabase; moving to a self-managed Supabase project is supported | Bolt Database: duplicating carries over structure only, no data | not included when a project is copied |
| Limits | Plain PostgreSQL needs a replacement for login, storage and edge functions | GitHub and Netlify settings do not carry over when duplicating | Secrets and deployment configuration are missing from a copy |
Checklist
Testing portability while nothing is urgent
10
In what order should these points get tackled?
First, whatever could already expose data today: RLS and secret keys. Migrations and access control follow, because every later change builds on them. Logs and portability come next, before operations begin. The decision guide walks through the questions and shows what is worth discussing next.
Decision path
Is the application ready for real data?
Answer based on what can actually be demonstrated.
All questions and results as a list
- Does the application store personal or business data?
- Yes, continue with: Is RLS active everywhere, and has the cross-test with two accounts passed?
- No, demo content only, Result: Continue as a prototype
- Is RLS active everywhere, and has the cross-test with two accounts passed?
- Yes, demonstrated, continue with: Are the frontend and the Git history free of secret keys?
- No or untested, Result: Close down the database first
- Are the frontend and the Git history free of secret keys?
- Yes, searched, continue with: Does the schema build completely from migrations?
- No or unchecked, Result: Revoke and relocate the keys
- Does the schema build completely from migrations?
- Yes, continue with: Do the database or server enforce every role?
- No, Result: Bring the schema into migrations
- Do the database or server enforce every role?
- Yes, Result: Ready for the next step
- No or unclear, Result: Move access control into the database
- Result: Continue as a prototypeWorth discussing: at what point real data starts flowing, and who triggers this check when it does.
- Result: Close down the database firstWorth discussing: which tables hold which data, which rule each policy reflects, and whether data has already been accessed.
- Result: Revoke and relocate the keysWorth discussing: which keys are affected and which calls need to move to the server.
- Result: Bring the schema into migrationsWorth discussing: capturing the current state as a baseline migration, building a test environment from it, and rolling out changes only through files from then on.
- Result: Move access control into the databaseWorth discussing: which role model the application needs and how an API test covers every role.
- Result: Ready for the next stepWorth discussing: logging, portability, monitoring, backups and who takes ownership going forward.
Where IT needs to take over a department-built application, A department built an app helps. What studies show about the quality of AI-generated code summarises what these investigations into generated code actually measure.
Who is liable when AI-generated code causes damage? covers liability questions. iiterate Technologies GmbH, based in Adenau, develops custom software and AI applications from architecture through implementation to operations, on a client's own infrastructure or in EU hosting, and hands over source code and documentation. To talk about an application: Contact.
Frequently asked questions
Is vibe coding with Lovable, Bolt or Replit unsuitable for organisations?
No. For prototypes, internal experiments and clarifying requirements, these tools are well suited and save real time. What matters is the transition: once personal data, roles or payments enter the picture, the application needs the same checks as any other software. The points covered here are a starting point, not a substitute for a security review.
Is the public Supabase key in the frontend a security problem?
Not on its own. Supabase describes the Publishable Key, formerly anon, as safe for browsers and apps. But it is only as secure as the row-level security policies behind it, since anyone can call the database API with it. Any secret key in the frontend is a problem, in Supabase's case the Secret Key or service_role, because it bypasses every policy.
Can a Lovable app run on an organisation's own servers?
According to the vendor's documentation, yes. The code can be synced through GitHub, cloned and run independently, the frontend on any infrastructure, the backend on managed or self-hosted Supabase. The limit sits at the platform services: doing without Supabase entirely means building a replacement for login, file storage and server-side functions.
Do these points also apply to Bolt and Replit?
Yes, wherever the application uses the same architecture. If a Bolt or Replit project calls Supabase directly from the browser, RLS and key separation apply in exactly the same way. If the logic runs on a dedicated server instead, the check shifts there, and the server has to authorise every request itself. Logging, migrations and portability apply to every application regardless.
Read on
Sources
- 01 CVE-2025-48757 Detail National Vulnerability Database (NIST), 2025 · nvd.nist.gov
- 02 CVE Record CVE-2025-48757 CVE Program, 2025 · cve.org
- 03 CVE-2025-48757 Primäre Offenlegung, 2025 · mattpalmer.io
- 04 Statement on CVE-2025-48757 Primäre Offenlegung, 2025 · mattpalmer.io
- 05 Row Level Security Supabase Docs, 2026 · supabase.com
- 06 Securing your API Supabase Docs, 2026 · supabase.com
- 07 Understanding API keys Supabase Docs, 2026 · supabase.com
- 08 Database Advisors Supabase Docs, 2026 · supabase.com
- 09 Database Migrations Supabase Docs, 2026 · supabase.com
- 10 Build your first API Supabase Docs, 2026 · supabase.com
- 11 The pg_tables view PostgreSQL Documentation, 2026 · postgresql.org
- 12 Env Variables and Modes Vite, 2026 · vite.dev
- 13 Security overview Lovable Documentation, 2026 · docs.lovable.dev
- 14 GitHub integration Lovable Documentation, 2026 · docs.lovable.dev
- 15 Deployment, hosting, and ownership options Lovable Documentation, 2026 · docs.lovable.dev
- 16 Manage your projects Bolt Help Center, 2026 · support.bolt.new
- 17 Projects and files Replit Docs, 2026 · docs.replit.com
Let us talk about your project
Whether it is a prototype, an internal tool or an AI application: describe briefly what you are building or want to take into production.