An app built with AI is not safe because the preview loads. Before you share the link or take money, check seven things. Paste one prompt at a time. Tell the tool to report first. Do not let it "fix" production while it still has the production password. The docs behind this list were checked 2026-10-22.
1. Secret keys stay off the browser
Vite exposes every VITE_ variable in the client bundle. Next.js does the same for NEXT_PUBLIC_. A name that looks private is not private if the prefix puts it in the JavaScript your visitors download. Stripe says only the publishable key (pk_) is safe in frontend code. Secret keys (sk_) and restricted keys are not. Supabase's row level security guide says the service role bypasses those rules, so it stays on the server. A publishable or anon key in the browser is expected, and it is only safe when the rules below are on.
Look through this project and report. Do not change files yet.
1. List every environment variable whose name starts with VITE_ or NEXT_PUBLIC_.
2. For each one, say whether the value is a secret or a value the provider says can live in the browser. Secrets include database passwords, a Supabase service-role or secret key, a Stripe secret or restricted key, and a Stripe webhook signing secret. Values that can be public, once the other controls on this checklist are on, include a Supabase publishable or anon key, a Stripe publishable key, and the Firebase web config.
3. Name the file where each secret is read. Do not print the secret values.
4. Do not rotate, delete, or deploy anything. Stop after the report.
2. Turn on Supabase row level security
Supabase's docs say a table in an exposed schema without row level security is readable and writable by any role that has a grant. Enable it on every exposed table. After it is on, the publishable key sees nothing until you add policies. Adding a policy does not, by itself, undo wide grants that already exist. On projects that still grant anon and authenticated by default, revoke the grants you do not want. A signed-in user should not be able to read another user's rows. Lovable and Bolt will create tables for you. Open the Supabase table list and check the lock, or paste the prompt.
Audit Supabase access in this project. Do not run SQL against production and do not change policies yet.
1. List every table in an exposed schema, usually public.
2. For each table, say whether row level security is enabled.
3. List the policies, and say what the anon and authenticated roles can select, insert, update, and delete.
4. Search the repo for a service-role key or secret key. Say whether any browser component, VITE_ variable, or NEXT_PUBLIC_ variable can see it.
5. Flag any table where one signed-in user can read another user's rows.
Stop after the report.
3. Take Firebase out of Test mode
When you create a database, Firebase lets you deny everyone (Locked mode) or grant access to all users (Test mode). The insecure-rules guide shows allow read, write: if true as the pattern that lets anyone overwrite the database, and the same warning for Realtime Database and Cloud Storage. It also says a rule that only checks request.auth != null lets any logged-in user touch data that is not theirs. Open the Rules tab. If you see if true, replace it with a rule that matches the owner's user id before you publish the app. The pages checked here describe that open access. They do not publish a countdown, so this guide does not invent one.
Audit Firebase Security Rules in this project. Do not deploy rules and do not change production.
1. Find every firestore.rules, storage.rules, and database.rules file. If the rules are not in the repo, say that I have to open the Firebase console.
2. Flag any rule that uses "if true", allows read and write with no condition, or sets ".read" or ".write" to true.
3. Flag any rule that allows every signed-in user (request.auth != null, or auth != null) without checking that the user owns that document or file.
4. Say what a stranger who is not logged in can read or write.
Stop after the report.
4. Check the user on the server
A login screen is not authorization. Hiding a button still leaves the request. Every route that reads or writes a row should know who is calling, from the session, and should refuse rows that belong to someone else. Database rules are the backstop. They are not a reason to skip the check in your own server code, especially for an action the browser triggers with a user id it chose.
Audit authorization in this project. Do not change code yet.
1. Find every server route, server action, and edge function that reads or writes user data.
2. For each one, say whether it checks that the caller is signed in, and whether it checks that the caller owns the row.
3. Flag any query that trusts a user id sent from the browser without comparing it to the session.
4. Flag any secret key imported by a file that also renders in the browser.
Stop after the report.
5. Verify the Stripe webhook signature
Stripe signs each webhook with the Stripe-Signature header. Verify it with Stripe's library, the raw body, and the endpoint's signing secret (it begins with whsec_). If the framework parses the body first, verification fails or, worse, you skip it and trust a forged "payment succeeded" event. The secret is different for test and live, and it must not use a VITE_ or NEXT_PUBLIC_ name. Do this before the payments guide becomes a live charge.
Audit the payment webhook in this project. Do not change code and do not call the payment provider.
1. Find the webhook handler.
2. Say whether it verifies the Stripe-Signature header with Stripe's library, using the raw request body and a signing secret.
3. Say whether that secret is read from a server-only variable, or from a VITE_ or NEXT_PUBLIC_ variable.
4. Say whether the handler marks an order paid only after verification succeeds.
Stop after the report.
6. Keep a backup the agent cannot eat
Supabase's pricing page says automatic backups are not included on Free. Pro keeps daily backups for 7 days. Render's free Postgres docs say those databases have no backups, expire 30 days after creation, and are deleted after a further 14-day grace period. If the only copy of your users is in a project an agent can edit, you do not have a backup. Turn on the provider's backup, or export a copy to a place the agent is not logged into, before you invite users. The pricing pages checked on 2026-10-22 do not state a Firebase backup quota, so this guide does not invent one. Check the Firebase console.
Report how this project is backed up. Do not delete, export, or modify production data.
1. Name the database, and say whether a separate development project exists.
2. Say whether the repo shows that automatic backups are turned on. If the repo cannot tell you, say that I have to check the provider dashboard.
3. List which files or tools in this workspace can see the production database password or service-role key.
Stop after the report.
7. Keep the agent away from production data
In July 2025 a Replit user's database lost data while the agent was working. Replit's own write-up says the agent deleted that data, development and production were not separated, and rollback did restore it. Replit says separate development and production databases are now the default, and that the agent cannot change production during development. Other tools do not have that switch. Give the agent a development project. Keep production credentials out of the chat and out of the .env file the agent can read. Paste this before a risky session.
Before you change anything in this project:
1. Tell me whether the database credentials in this workspace point at production.
2. If they do, stop. Do not run migrations, deletes, or updates against that database.
3. Tell me what a separate development project would need, and which credentials I should swap in.
4. Do not create, drop, or empty any table until I confirm you are on the development project.