Before your first users arrive, check five things: no secret keys in browser code or the repository; a user sees only their own data even when they swap an ID in a request; access rules are enforced in the database (for example, RLS in Supabase); a backup exists and you know how to restore from it; and your company owns the accounts, the domain and the code. These checks don’t make an app invulnerable, but they close the mistakes that are hardest to undo after launch.
Contents
- Why a working prototype isn’t ready for real people yet
- Keys and secrets: anything in the browser is public
- Data access: does each user see only their own
- Money and limits: what might show up on the bill
- Data, backups and schema changes
- Ownership: accounts, code and the option to leave the platform
- Pre-launch checklist and what comes next
Why a working prototype isn’t ready for real people yet
If you built your product with Cursor, Claude Code, Lovable, Bolt or Replit, that is a real achievement: an idea became a working app you can show to customers and investors. What is often called vibe coding is a great way to test hypotheses. The point is that a prototype and a product for real people solve different problems.
An AI assistant builds what you described — a scenario that works in a demo. It doesn’t know that one of your future users will open the browser’s developer tools, change an order number in a request or fire a thousand calls at a paid feature. Unless you ask explicitly, those scenarios stay untested — not because the AI is “bad”, but because they were never part of the task.
Demo
- One user — you
- Test data nobody minds losing
- A bug can be fixed and shown again
- No money moves
Real users
- Many people, some of them curious
- Other people’s personal data and your responsibility for it
- A leak can’t be “rolled back”
- Payments, paid APIs and provider bills
Keys and secrets: anything in the browser is public
Code that runs in the browser can be read by any visitor. Build tools say so directly: in Vite, variables prefixed with VITE_ are bundled into client code and must not contain API keys; in Next.js, variables prefixed with NEXT_PUBLIC_ are inlined into the JavaScript the browser receives. If a key to a paid or privileged service ends up in such a variable, it is effectively published.
Not every key is secret. Supabase — a backend that AI builders, among others, connect to — has a publishable key (publishable, formerly anon) meant for the browser; its safety depends on row-level access rules in the database. It also has a secret key (secret, formerly service_role) that bypasses every Row Level Security policy, and the documentation explicitly says never to put it in a browser, a shipped app or source control. Keys for LLM providers, payment services and SMS gateways belong on the server only.
Search the built site
Open the app, go to developer tools → Sources and Network, and search for
secret,service_role,sk-,api_key.Search the repository and its history
A key removed in the latest commit still sits in history. GitHub secret scanning checks the full history of every branch; for organization-owned private repositories it requires a paid plan.
Check
.gitignore.envfiles with real values must not be committed.Rotate, don’t just delete
If a key leaked, reissue it with the provider. Once it is revoked, rewriting Git history is usually optional.
Data access: does each user see only their own
The first entry in the OWASP API Security Top 10 (2023 edition) is Broken Object Level Authorization: the server accepts an object ID but never checks whether this user may access this object. In practice the URL or request contains order_id=1042, the user changes it to 1043 and sees someone else’s order. The fifth entry is Broken Function Level Authorization: a regular user calls an admin function because the button was hidden in the interface but the server never checked the role.
If your data lives in Supabase or another PostgreSQL database the browser talks to directly, Row Level Security policies are your protection. Supabase states the rule firmly: enable RLS on every table in an exposed schema; a table without RLS is readable and writable by any role with a grant on it. A table with RLS enabled and no policies at all is inaccessible through the publishable key — a safe default.
Two test accounts
Create users A and B. User A creates a record — an order, a note, a document.
Swap the ID
Sign in as B and try to open, edit and delete A’s record by its ID — through the page URL and through the request in the Network tab.
Other people’s functions
As a regular user, call admin actions: changing roles, deleting, exporting.
Signed out
Repeat the requests without authentication. The expected answer is a refusal — not an empty success and not data.
Review RLS
In the database dashboard, confirm RLS is enabled on every table with user data, and read each policy: who it allows to do what.
Money and limits: what might show up on the bill
OWASP calls out unrestricted resource consumption separately: every request uses CPU, memory and bandwidth, and some call paid services — SMS, verification checks, language models. If a “generate answer” feature has no limits, a single script can run up your provider bill and take the service down for everyone else.
- Every paid provider (LLM, SMS, maps, hosting) has spending limits or overspend alerts configured.
- Endpoints that call paid APIs require sign-in and are rate-limited per user.
- Input size is capped: text length, file size, number of items per request.
- Payment is confirmed by the server from the payment provider’s notification, not by the browser returning from the checkout page.
- Sign-up and forms are protected against mass automated submission.
Data, backups and schema changes
A backup nobody has tried to restore is a hope, not a plan. Make sure backups run automatically and are stored separately from the main database, and restore one into a separate environment at least once. Write down how long it took and which steps it needed.
- Test and production databases are separate; the AI assistant experiments on the test one.
- Schema changes are migrations in the repository, not clicks in the production console — otherwise they can’t be repeated or rolled back.
- You collect only the personal data the scenario cannot work without, and you know where it is stored.
- Server errors land in a log or monitoring service, and you hear about failures before your users do.
Ownership: accounts, code and the option to leave the platform
Fast builders are convenient because they take care of hosting and the database. The flip side is that the product can end up tied to one person’s account or to a platform it is hard to move away from. You want to know this before customers depend on the product.
- Builder, database, hosting and domain accounts use a company address, with two-factor authentication on.
- The code is synced to a Git repository owned by your company.
- The product can be built and run outside the platform from instructions in the repository — or you know it can’t, and that is a conscious decision.
- There is a list of every connected service and key, with where each one is stored.
- A professional team, if you bring one in, can get access without you handing over personal passwords.
Pre-launch checklist and what comes next
- No secret keys in browser code or repository history; leaked ones have been reissued.
- User B cannot read, edit or delete user A’s data.
- Admin functions check the role on the server.
- RLS is enabled on every table with user data, and the policies have been read.
- Paid APIs are rate-limited, and providers have spending limits.
- Payment is confirmed by the server.
- Restoring from a backup has been tested.
- Test and production are separate; the schema changes through migrations.
- The company owns the accounts, the domain and the repository.
If most items pass, the sensible next step is a review of the modules where a mistake costs the most: access and roles, data, payments, build and deployment. The outcome should be a risk map and a decision for each module — keep, fix or replace — not a proposal to rewrite everything.

