equityflowunderstand the flow and follow it

The all-in-one path: vibecoding with Replit, Lovable and Bolt

The fastest path to a live product: let the platform host everything. What Replit, Lovable and Bolt genuinely handle for you, the application security you still own even when it is all managed, going live with a custom domain, and when to graduate to running your own stack.

The fastest way to go from idea to a live, working product is to let a platform handle everything: Replit, Lovable and Bolt host your app, run your database, manage deploys and hand you a URL, all without you ever touching a server. For most people validating an idea, this is the right path, and it is wonderful. It also breeds a dangerous myth: that because the platform "handles everything", there is nothing left for you to get right. There is, and it is the part that protects your users. This guide is the honest version of the all-in-one path: what you genuinely do not have to worry about, and the short list of things you absolutely still do.

What the platform actually handles for you

The appeal is real, and worth being precise about. When you build on Replit, Lovable or Bolt, the platform takes care of the unglamorous infrastructure that used to eat a founder's first month:

  • Hosting and servers. There is no machine to rent, patch, or keep alive at 3am. Your app runs on the platform's infrastructure.
  • Deployment. Going live is a button, not a pipeline. No SSH, no nginx, no build scripts to debug.
  • The database and backend. Most of these tools wire up a managed database (often Supabase or Firebase under the hood) and auth so you are not configuring Postgres by hand.
  • TLS and the domain. HTTPS is automatic, and pointing a custom domain is usually a settings page, not a DNS tutorial.
  • Scaling the basics. A sudden trickle of real users will not require you to learn load balancing on day one.

This is genuinely most of the operational work, gone. It is why a non-technical founder can now ship something real in a weekend. If your goal is to validate an idea, do not over-engineer: take the easy path and get it in front of people.

What you still own (the part nobody hosts for you)

Here is the honest correction to "Replit means no security". The platform secures its own infrastructure. It does not, and cannot, secure the logic of your application, because only you know who is allowed to do what in it. Infrastructure security is handled. Application security is still entirely yours, and it is exactly the kind that leaks user data when skipped. The short, non-negotiable list:

  • Access control. The platform will happily let your code return any row in the database. Whether user A can read user B's private data is a rule only your app enforces. On hosted stacks this usually means Row Level Security, and it is off until you turn it on.
  • Never trusting the frontend. The price, the "is premium" flag, the user id, all of it can be edited in the browser before it reaches your backend. The platform does not check this for you.
  • Secrets. Your Stripe key, your API tokens: if they end up in the frontend bundle or a public repo, the managed hosting does not save you. Keep them in the platform's secrets store, never in client code.
  • What your endpoints return. A managed backend will return exactly the fields you tell it to, including the ones the UI hides. Over-fetching leaks data straight into the network tab.

The two failures that bite hosted builders hardest are trusting the frontend and broken access control, so the next two sections are the full treatment of each. For the rest (injection, leaked data, forgotten doors), the dedicated security guide is the complete reference, and you should read it before you have real users.

Never trust the frontend: the checkout and price-tampering trap

Here is the single most important sentence in this entire part. The frontend is a suggestion, not a fact. Everything your app sends from the browser, the price in the cart, the quantity field, the is_admin flag, the "you are a premium user" boolean, every hidden form field, every disabled button, can be read, changed, and replayed by anyone who opens the page. Not by hackers in hoodies. By a curious customer with the browser DevTools that ship in Chrome by default. If your server believes what the browser tells it, your server is taking orders from strangers.

AI agents ship this constantly, because in a demo the frontend and backend are written by the same hand and they trust each other. The model wires the price into the React state, passes it to the checkout call, and the server charges it. It works. It demos beautifully. It is also a wide-open door.

The attack, step by step

Imagine your app sells a course for $99. The agent built a checkout that posts this to your server:

POST /api/checkout { "productId": "course-pro", "price": 99.00 }

An attacker does not need any special tools. They:

  1. Open your checkout page and press F12 to open DevTools.
  2. Go to the Network tab and watch the request fly when they click "Pay".
  3. Right-click it, choose "Edit and Resend" (a one-click feature in Firefox and Chrome), and change "price": 99.00 to "price": 0.01.
  4. Your server reads price from the request, tells Stripe to charge one cent, and emails them the course.

Same trick, different field, same outcome:

  • Discounts: they add "discount": 100 and pay nothing.
  • Quantity: they send "quantity": -1 and the cart total goes negative, sometimes refunding them.
  • Entitlements: the signup request includes "plan": "free", so they change it to "plan": "enterprise" and unlock everything for free.
  • Privilege: the profile-update request includes the user object, so they tack on "is_admin": true and your server, blindly saving every field it receives, makes them an admin. This is called mass assignment, and it is one of the most common ways vibecoded apps get fully taken over.

The two cousins: hidden buttons and forgotten demo pages

"Feature gating" done in the frontend is the same mistake wearing a costume. If your premium feature is protected only by hiding the button from free users, the feature is not protected at all. The button is gone, but the API endpoint it would have called is still live. A non-paying user opens DevTools, finds the endpoint in your code, and calls it directly. Hiding is not securing.

The other cousin is the demo or preview page left in production. Agents love to scaffold a /demo, /admin-preview, or /test route with auth turned off "so you can see it working". If that route still touches real data and real accounts when you launch, you have shipped a backdoor that needs no password.

The one principle that fixes all of it

The server must decide every price, permission, quantity, and entitlement itself, from data it trusts, and ignore what the client claims. Never read a price from the request. Read the productId, then look up the real price in your own database. Never read "is this user premium" from the browser. Check it server-side against your subscription records for the currently logged-in user, identified by their session, not by an ID they sent you.

How to fix it

  • Price server-side, always. The client sends what they want to buy (an ID), never how much it costs. Your backend looks up the amount.
  • Re-check authorization on every single action. For every request that reads, changes, or buys something, ask on the server: is this logged-in user allowed to do this, right now? Do not trust a role or flag sent from the browser.
  • Never auto-save the whole request body. Accept only an explicit list of fields a user may change (name, email), and never let is_admin, plan, or balance be set from input. This kills mass assignment.
  • Validate values as hostile. Reject negative or zero quantities, absurd amounts, and unexpected currencies. Assume every field came from an attacker, because it can.
  • Remove demo and preview backdoors before launch. Grep your routes for anything with auth disabled. Delete it or lock it behind real login.
  • Tell your agent the rule explicitly: "Treat all client input as untrusted. Look up prices and entitlements server-side from the database. Authorize every action against the session user." Agents will follow this if you say it; they will skip it if you do not.

If you remember one thing from this part: the browser works for the attacker too. Decide what matters on the server, every time.

Broken access control: changing an ID and reading everyone else's data

This is the one. If you read a single chapter in this part, read this one. Broken access control sits at number one on the OWASP Top 10, and it is the single most common way a vibecoded app leaks every customer's data at once. It is also the easiest to understand. There is no clever cryptography to break. The attacker simply asks for data that is not theirs, and your app hands it over.

Access control is the rule that says "you can only see and touch your own stuff." The bug is forgetting to enforce that rule on the server. AI builders forget it constantly, because in the demo there is one logged-in user looking at their own data, and the code "works." Nobody tested what happens when a second person pokes at the first person's records.

Flavor one: IDOR, the increment attack

IDOR stands for insecure direct object reference. It sounds technical. It is not. Say your app shows an invoice at this address:

GET /api/invoices/1042

Your code looks up invoice 1042 and returns it. The problem: it never checked whether invoice 1042 belongs to the person asking. Here is the full attack, and anyone can do it from their browser:

  1. An attacker signs up as a normal customer and opens their own invoice. They see the address bar (or the network tab) say /api/invoices/1042.
  2. They change 1042 to 1041 and hit enter.
  3. Your server returns someone else's invoice: their name, email, address, what they bought, the last four of their card.
  4. The attacker writes a five-line script that walks from 1 to 100000 and saves every response. In a few minutes they have your entire customer table.

The same bug leaks private messages (/api/messages/88), uploaded files, support tickets, and medical or financial records. Sequential numeric IDs make it trivial, but switching to random UUIDs does not fix it. It only means the attacker has to find the IDs elsewhere (shared links, old emails, API responses) before reading data they should never see. UUIDs are obscurity, not access control.

Flavor two: writes with no ownership check

Reads leak data. Writes are worse, because the attacker changes your data. If your app lets the client call POST /api/orders/1042/cancel or DELETE /api/posts/77 and the server only checks "are you logged in?" instead of "do you own this?", then any logged-in user can cancel anyone's order, delete anyone's post, or edit anyone's profile by guessing IDs.

The nastiest version is the price or role field the client gets to set. A checkout that trusts a client-sent price or total lets a stranger change one number in the request and buy your $99 product for $0.01. A signup that trusts a client-sent role field lets anyone register and add "role": "admin" to make themselves an administrator. The server must decide price and role from its own records, never from what the browser sends.

The Supabase and Firebase trap

This one hits vibecoders especially hard. Tools like Supabase and Firebase put a database directly behind a public API, and the browser talks to it using a key (the Supabase anon key) that is meant to be public and ships in your client-side code. That is fine by design, but only if Row Level Security (RLS) is turned on and written correctly.

The catch: tables you create through the SQL editor or migrations have RLS off by default. With RLS off, that public anon key can read and write the entire table straight from any browser. An attacker opens your app, copies the project URL and anon key out of the page source (they are right there), and queries every table directly, bypassing your app's screens entirely. This is exactly how the well-documented wave of leaked AI-built apps happened: real databases of emails, messages, and private records, readable by anyone with two strings sitting in plain sight. A permissive policy like "allow all" is the same disaster with extra steps.

Why AI builders ship this

An AI agent optimizes for a working demo. The happy path (one user, their own data) works, so it stops. Authorization is invisible logic that has to be repeated on every single endpoint, for every role and every record, and the agent has no way to test the unhappy path unless you tell it to. So it skips the check, or generates the table with RLS disabled, and everything looks perfect until a stranger increments an ID.

How to fix it

  • Default deny. Every read and every write starts from "no," and you explicitly allow only the owner (or the right role). If you forgot to write a rule, the answer is no, not yes.
  • Trust the session, never the client. Get the user's identity from their logged-in session on the server. Never accept a userId, role, or price from the request body and trust it.
  • Check ownership on every object. Before returning or changing record 1042, confirm in your query that it belongs to the current user: where id = 1042 and owner_id = current_user.
  • Turn RLS on for every table and write strict policies. Run ALTER TABLE your_table ENABLE ROW LEVEL SECURITY; on every table, add a policy tying rows to auth.uid(), and resolve every "RLS disabled" warning in the dashboard. See the Supabase RLS docs.
  • Test it yourself. Make two accounts. Log in as user A, copy a request, then log in as user B and replay it against user A's IDs. If you see A's data, you are vulnerable. Tell your agent to write this test.

Going live, the easy way

This is where the hosted path shines. On Replit, Lovable or Bolt, going live is roughly: click Deploy, wait, and you have a public URL on the platform's domain with HTTPS already working. To use your own domain, you buy it (Namecheap, Cloudflare, Porkbun, around 10 to 15 dollars a year for a normal name) and paste it into the platform's domains settings, which tells you the one DNS record to add. Within minutes to a couple of hours, your app answers at yourname.com with a valid certificate.

What you skip compared to the full-control path: no server to provision, no nginx or reverse proxy, no manual TLS with certbot, no deploy script, no separate staging server to maintain. The platform's preview and production environments cover the staging-versus-production split for you. Before you point a domain at it, do the one thing that is still on you: run the security checklist, especially access control and the frontend-trust checks, because a public URL means the whole internet can now poke at it.

The limits, and when to graduate

The all-in-one path has a ceiling, and it is worth knowing before you hit it. The trade-offs:

  • Lock-in. Your app is shaped by the platform's conventions. Check early whether you can export real, runnable code that you own, because the day you need to leave is the wrong day to discover you cannot.
  • Cost at scale. Hosted convenience is cheap at small scale and gets expensive as usage grows. The managed database and per-seat or per-run pricing that felt free at ten users can sting at ten thousand.
  • The control ceiling. Custom infrastructure, unusual backends, heavy background jobs, fine-grained performance work: at some point the abstraction that helped you starts getting in the way.

None of this is a reason to avoid the hosted path. It is a reason to start there and graduate deliberately. The signal to move is concrete: you are fighting the platform more than it is helping, the bill outgrows a small server, or you need control it will not give you. When that day comes, the full-control path is where you go, with your own server and an agent like Claude Code, and you bring the architecture and security discipline with you.

Going deeper or outgrowing the platform? The full-control path covers owning the whole stack, and the security guide is the complete reference for protecting your users.

← All deep dives