soft-shell crabvietnamese mud crab
6
15 Comments

Why vibe-coded apps are still leaking user data in 2026

Last month a founder showed me their Lovable app, proud of what they built. "It's ready to launch," they said.

I asked them to open DevTools. Their Stripe keys were sitting right there in the client bundle. Visible to anyone.

That is the misconception I keep running into:"I built it on Lovable/Bolt/Replit, it's ready to launch."

As a software engineer I want to share an honest take
on this. Genuinely open to pushback.

What these tools are actually good at

Incredible for validating an idea fast. Build it, put it on Product Hunt or Indie Hackers, get real users to test it, collect feedback. That is the exact right use case.

The problem is when founders treat the prototype as the product.

The architectural gaps, by platform

Lovable

  • Connects your React frontend directly to Supabase with no backend layer in between
  • Database access rules are auto-generated and frequently misconfigured
  • API keys sit inside the client bundle, visible to anyone who opens DevTools
  • CVE-2025-48757 exposed 170+ live apps: names, home addresses, financial records, Stripe keys

Bolt.new

  • Backend locked to Node.js/Express only, no flexibility as your stack evolves
  • Generated code frequently described as "half done" in production scenarios
  • New infrastructure (2025), compliance-heavy workloads still need migration to established cloud

Replit

  • In July 2025, Replit's AI agent deleted a live production database during an explicit code freeze, then misreported what it had done
  • Free tier exposes your source code publicly
  • Better for learning than for production systems handling real user data

The pattern across all of them

  • A 2025 scan of 5,600+ vibe-coded apps found thousands of vulnerabilities and hundreds of exposed secrets
  • No server-side layer means payment processing and permission checks happen client-side
  • No separation between development and production environments by default

My honest take as an engineer

Use these tools to get your mental model out of your head and into something testable. A working prototype is genuinely useful. It shows exactly what you need and what matters to you as a founder.

The prototype tells you what to build. It does not tell you how to build it safely. Those are two completely different problems and conflating them is where most founders get hurt.

Notion, Figma, and Canva did not get to millions of users by prompting harder. They have been running for 10 to 15 years with real technical teams making real
architectural decisions every single day.

Your product deserves the same thinking, even at day one.

Have you shipped on one of these platforms and hit this wall? Or do you think the tools have gotten good enough that this concern is outdated?

on August 7, 2026
  1. 1

    Everything here assumes a human pasted the key. Worth adding the version that's specific to how this code gets written now: the agent has it too.

    When a founder pastes an sk_ into a prompt to get something working, that key lands somewhere the codebase isn't. Claude Code writes full session transcripts to ~/.claude/projects/*.jsonl — every message, every tool call, every file it read, kept indefinitely. Every agent tool keeps history somewhere similar. Those files aren't in .gitignore, because they were never in the repo.

    So m_montazeri's rotate-don't-remove applies twice over. You can scrub the bundle, scrub git history, and the key is still sitting in a transcript from three weeks ago that nobody thinks of as a place secrets live. It gets swept into backups, and on a shared or synced machine it travels.

    The habit that avoids the whole class: keys go in the environment, never into a prompt or a file the agent reads. Export it, let the CLI pick it up, and the agent can use the key without ever having seen the string.

    Smaller second thing: an agent generating an RLS policy is optimizing for "the query returns rows." A missing policy satisfies that objective too.

  2. 2

    one thing worth separating in the stripe example: pk_ and sk_ differ by one character and they are not the same finding. the publishable key is designed to sit in the client bundle, so pk_live_ showing up in devtools is not a leak. an sk_ in there is the real emergency, since it reads your full customer list and charge history and can create charges and issue refunds. we build affiliate software on stripe so this is daily work for us, and my worry with treating "stripe keys in the bundle" as one thing is that the founder who learns pk_ was fine is exactly the one who shrugs at an sk_ later.

    1. 1

      You are right to separate them. What we actually saw was an sk_ not a pk_. The reason it ended up in the frontend is the most common one with non-technical founders: they open Stripe, see two keys, do not know the difference, and paste both because the app was not working with just one. Not negligence in the way they understand the word. Just someone doing what made sense in the moment.

      We talk to founders every week and a big part of those conversations is teaching them how a properly built system handles this.
      Lovable even acknowledges in their own docs that their frontend architecture means secrets cannot be stored safely client-side. Which tells you everything about how structural this problem actually is.

      Your distinction is correct and important. What I would add is that the architecture makes sk_ exposure almost inevitable for someone who does not know that sensitive integrations should never originate from the browser.

  3. 1

    The prototype as product trap is the one I see most often too. Speed tools are excellent for proving people want the thing, and terrible at teaching which secrets belong on the server. What made your Stripe example stick is how silent the failure is: the app works, DevTools just happens to also hold the keys. One filter I use before calling anything launch ready is whether a stranger with only the client bundle and network tab could extract money or personal data. If yes, it is still a demo.

    1. 1

      That filter is a good one and it catches the most visible layer. But the DevTools test was never the whole point. It was just the fastest way to show a founder how shallow the foundation is.

      A founder can pass your filter completely. No keys in the bundle, RLS configured, every secret behind an Edge Function. And still have an app with no backend layer separating concerns, no connection pooling, no query optimization, no rate limiting, no horizontal scaling path, and generated code that takes the shortest route to working rather than the right route to production.

      The security gap is the symptom that is easiest to see and explain. The architecture is the actual problem. An app built this way was designed for one person clicking through a demo. The moment real users arrive simultaneously, real data accumulates, real payments process, and real load hits the system, the decisions that were never made start showing up all at once.

      The founders who feel this most are not the ones building simple tools. They are the ones building something genuinely complex, a marketplace, a platform, anything with multiple user types and real business logic, who reached the limit of what a prompt can specify and realized the gap between what the tool generated and what the product actually needs is not a feature gap. It is an architectural one.

      The DevTools example just makes it visible in ten seconds. The real conversation is everything underneath it.

  4. 1

    "Vibe-coded" really means "built without measurement." No monitoring, no logging, no visibility into what's actually happening. That's why data leaks hide for months.

    If you had a single line of logging on every user data access - where it's being sent, who touched it, what format it's in - you'd spot these patterns in hours. But measurement requires you to know what to look for. So you end up flying blind until someone external finds the problem.

    The security issue isn't actually the technical vulnerability. It's that you have zero visibility into your own system's behavior. That's not a coding problem. That's a measurement problem. You didn't build a thing to see what the thing does.

    1. 1

      The measurement framing is interesting but I would push back on the core claim. Logging and monitoring tell you something went wrong after it went wrong. By then the damage is already done. A leaked sk_ that has been in a bundle for three weeks has already been extracted by anyone who wanted it. No log entry changes that.

      The real problem is not visibility into behavior. It is not following the standards that prevent the behavior in the first place. AppSec exists as a discipline specifically because security is not something you bolt on after building. It is a set of architectural standards baked in from the start. Server side secrets handling, proper authentication layers, RLS configured with intent not generated by an AI, input validation, least privilege access. These are not monitoring problems. They are design decisions made before a single line of code is written.

      Monitoring is valuable when a vulnerability exists beyond what standards can prevent. But if the standards were not followed in the first place, logging tells you the house burned down. It does not stop it from burning.

      No system can claim 100 percent security. But the gap between a vibe coded app and a properly architected one is not a monitoring gap. It is a standards gap. And that gap is structural from day one.

  5. 1

    Everything here is about web, where the fix is a redeploy. The mobile version is worse and founders do not expect it.

    If a key ships inside an app binary it is on people's phones and you cannot pull it back. You push an update, it waits for review, then you wait for people to install it. Some never do. Months later a share of your users are still running the build with the key in it, so you have to rotate the key and treat the old one as public.

    On mobile the cost is not a redeploy. It is a key rotation plus a forced upgrade path you probably did not build.

    1. 1

      This is the part nobody thinks about until it is too late. Web feels fixable because a redeploy takes minutes. Mobile reframes the entire problem because you cannot take back what is already on someone's device.

      The forced upgrade path is the thing that stings most. Most early stage apps do not build minimum version enforcement because it feels premature. Then a key ships in a binary and suddenly you need to force every user onto a new build immediately but the infrastructure to do that was never built. You are doing an emergency key rotation while simultaneously building a forced upgrade system under pressure with users actively hitting the old build.

      The practical takeaway for any founder building on mobile is to treat every key in a binary as already public. Not because it will definitely be extracted but because the cost of being wrong is so much higher than the cost of routing it through a professionals from day one.

  6. 1

    The pk_ / sk_ correction above is the right one, and there is a step that usually gets skipped once a key has been in a bundle at all: rotate it, do not just remove it.

    A JS bundle is public the moment it ships. Browsers cache it, CDNs mirror it, scrapers archive it. Editing the file only stops new copies. If an sk_ was ever in there, treat it as disclosed: roll the key first, then check the API logs for calls from addresses you do not recognise, and only then worry about the code. Same for a Supabase service_role key, which is the one that bypasses row-level security entirely.

    The other thing about the client-direct architecture is subtler than "keys leak". When the browser talks to the database, your schema is public too. Anyone can read the bundle, learn every table and column name, and then send whatever query they like with the anon key. Row-level security is not a hardening layer in that design, it is the entire authorization system, and it was written by a generator that cannot know your intent.

    The cheap test is to stop checking it from the dashboard, where you are effectively an admin, and drive it from outside instead: pull the anon key out of your own bundle, curl the REST endpoint for the tables you assume are protected, and try reading another user's rows by id. Then repeat while signed in as a second real account. Ten minutes of that tells you more than any scanner. The two findings that come up constantly are tables where RLS was never enabled at all, and policies keyed off a value the client itself supplies.

    None of this is an argument against building the first version that way. It is an argument for knowing exactly which line you crossed the moment a stranger's data or a payment entered the system, because that line usually gets crossed a few weeks before anyone notices.

    1. 1

      The rotate first point is the one that gets skipped every single time. Removing the key from the code feels like fixing it because the visible problem is gone. But the bundle already shipped. It is already cached. Anyone who pulled it before the edit still has the key. Rotation is the only actual fix and it has to happen before anything else.

      The schema exposure point is the one founders understand least. They think RLS is protection. What they do not realise is that when the browser talks directly to the database, RLS is not one layer of a defense stack. It is the only layer. And it was generated by an AI that had no context about who should see what. The curl test you described is exactly how we demonstrate this to founders who are not convinced. Pull your own anon key, hit the endpoint, read rows that should not be yours. Ten minutes and the conversation changes completely.

      The last line is the most important one. The line between prototype and production gets crossed quietly. Usually a few weeks before anyone looks.

  7. 1

    You’ve clearly seen a lot of founders move from prototype to launch.

    After those conversations, what changed most in how you think founders should decide whether something is actually ready to become a real product?

    1. 1

      The biggest shift in how I think about it comes down to three questions. Not features, not design, not user feedback. Three questions about what breaks when
      something goes wrong.

      The first is consequences of failure. A prototype failing is fine. Nobody loses data, nobody gets charged, nobody's business depends on it. The moment real users are trusting it with their information or their money, failure has a completely different cost. That is the first line a product has to cross before the architecture underneath matters.

      The second is simultaneous load. A prototype is built assuming one person is using it at a time. Production means multiple users hitting the same system simultaneously. This is where most vibe coded apps break first because there is no queue management, no connection pooling, no rate limiting at the server layer. Ten users at once on a Lovable app can bring down a Supabase instance. Not because the idea is bad. Because the architecture was never built for that reality.

      The third is data permanence. A prototype can reset. Production cannot. The moment a user creates an account, makes a payment, or stores something they expect to find again later, you have entered permanent data territory. Losing or corrupting that data is not a bug you fix in the next sprint. It is a legal and
      reputational event.

      Most founders cross all three lines without noticing because the product looks exactly the same from the outside. The architecture underneath is what changes
      everything. And by the time they notice, real users are already inside it.

      I am actually putting together a detailed breakdown of each of these three stages with real examples of what breaks and when. Will share it here when it is
      ready if that would be useful.

      1. 2

        The interesting distinction is that the product can look identical from the outside while the risk profile changes underneath.

        The transition from prototype to something people can rely on is where those hidden assumptions start becoming visible.

        1. 1

          Exactly. And the hidden assumptions are almost always the same ones. The founder assumed one user at a time. They assumed data could be rolled back. They assumed failure would be visible before it was consequential. None of those assumptions are wrong for a prototype. All of them are dangerous in production. The product did not change. The assumptions stopped being true.

Trending on Indie Hackers
I Just Discovered My Analytics Numbers Are Mostly Fake. Here Is Why. User Avatar 95 comments Co-founders suck… User Avatar 73 comments I built an AI that finds the right product for your customers User Avatar 41 comments The Capture Trap User Avatar 39 comments I built a tool to find people already talking about problems your product solves User Avatar 34 comments Solo-built Pistly for months. Launching on PH this week and I still don't know if the market wants it. User Avatar 33 comments