# Michael Bires — Vibe-Code Rescue & Emergency Web Repair > Senior full-stack engineer. I audit, fix, harden, and deploy broken AI-built apps and hacked WordPress sites. Fixed prices, audit-first. I am a senior full-stack engineer specializing in rescuing AI-generated (vibe-coded) apps and cleaning up hacked WordPress sites. I diagnose before I touch a line of code, work at fixed prices, and harden every project so it doesn't break again. ## Services - [Vibe-Code Rescue](https://michaelbir.es/services/vibe-code-rescue): Your AI-built app works in the demo but breaks in production. - Triage / Audit ($299 — 48 hours): Know exactly what's wrong before spending more. - Priority Fixes ($799 — 3–5 days): The audit plus the fixes that unblock you. - Hardening & Deploy ($1,799 — 1–2 weeks): From fragile prototype to production-ready. - [Web Ambulance](https://michaelbir.es/services/hacked-wordpress-cleanup): Site hacked or down? Get a senior engineer on it now. - Emergency Cleanup ($349 — typically < 24 hours): Full cleanup with the root cause actually closed. - Care Plan (from $79/mo): So this never happens again. ## Fix-it guides - [Works in the Demo, Breaks in Production: The 6 Vibe-Code Failures](https://michaelbir.es/fix/ai-app-breaks-in-production): The six most common reasons AI-built apps (Lovable, Bolt, v0, Replit, Cursor) fail in production — and how to triage them without going in circles. - [How to Deploy a Lovable App to Production (Without It Breaking)](https://michaelbir.es/fix/deploy-lovable-app-production): A production deployment checklist for Lovable apps: custom domains, environment variables, Supabase configuration, and the failures that only appear after going live. - [Lovable App Broke After an Update? How to Recover Without Burning Credits](https://michaelbir.es/fix/lovable-app-broke-after-update): Your Lovable app worked yesterday. After a platform update, a deploy, or a 'security fix' prompt, everything broke. Here's how to triage regressions and when to stop prompting. - [Lovable Emails Not Sending? Fix Signup & Password Reset](https://michaelbir.es/fix/lovable-email-not-sending): Users sign up but never get a confirmation email? Password reset links go nowhere? Supabase's built-in email is rate-limited and lands in spam — here's how to fix it. - [Lovable Google Login Not Working? Fix the localhost Redirect](https://michaelbir.es/fix/lovable-google-login-not-working): Sign in with Google works on localhost but redirects to localhost:3000 on your Lovable preview or custom domain? It's almost always Supabase URL configuration — here's the fix. - [Stripe Webhooks Not Firing After Deploy? Here's How to Fix It](https://michaelbir.es/fix/stripe-webhook-not-firing): Payments succeed but orders never complete? The 5 reasons Stripe webhooks stop firing after you deploy — and how to fix each one. - [Supabase RLS Not Working (or Disabled)? Fix It Before Someone Finds It](https://michaelbir.es/fix/supabase-rls-not-working): Why ~70% of Lovable apps ship with row-level security disabled, what CVE-2025-48757 means for your app, and how to enable RLS without breaking everything. - [Why Google Can't See Your Vibe-Coded Site (And How to Fix It)](https://michaelbir.es/fix/vibe-coded-site-not-indexed-google): AI-built sites are client-side rendered — Google may index an empty shell and AI crawlers see nothing at all. Here's how to check and how SSR fixes it. ## Case studies - [Compromised WooCommerce Store Cleaned & Hardened in Under 24 Hours](https://michaelbir.es/case-studies/woocommerce-hack-cleanup): Three backdoors removed after a scanner plugin failed twice, entry point closed, and the store back online clean — all in under 24 hours. - [CVE-2025-48757: 170+ Lovable Apps With Open Supabase Tables](https://michaelbir.es/case-studies/lovable-supabase-rls-security-audit): Matt Palmer's coordinated disclosure found 303 exposed endpoints across 170 Lovable projects — including Linkable, a Lovable marketing site. What happened, what leaked, and how I fix the same pattern for founders. - [Documented: Stripe Bypass on Linkable (CVE-2025-48757 Follow-Up)](https://michaelbir.es/case-studies/lovable-stripe-webhooks-saas): Matt Palmer's May 2025 re-test of Linkable showed payments could be marked 'paid' via a direct Supabase POST — bypassing Stripe. The same RLS class of bug breaks legitimate webhook flows in Lovable SaaS apps. - [Documented: Lovable Google Login Redirects to localhost:3000](https://michaelbir.es/case-studies/lovable-google-login-custom-domain): A published Medium walkthrough by Ishwar Rimal (Intuit) describes the exact failure: Google OAuth works locally but redirects to localhost on Lovable preview URLs. Industry triage data puts this at ~15% of broken Lovable deploys. - [Documented: Non-Technical PM Stuck in AI Debug Loops for 3 Months](https://michaelbir.es/case-studies/lovable-production-rescue-after-ai-loop): Andreea Papillon's published 3-part series describes building with AI without dev resources: 90% backend bugs, auth taking 3–5× longer, and Claude/Cursor going in circles for days. The rescue pattern I sell exists because of stories like this. ## Contact - [Contact form](https://michaelbir.es/contact): describe the problem, get a reply within 24 hours - Email: hello@michaelbir.es - LinkedIn: https://www.linkedin.com/in/michael-bires/ ## Optional - [llms-full.txt](https://michaelbir.es/llms-full.txt): full text of every guide and case study in one file --- # Works in the Demo, Breaks in Production: The 6 Vibe-Code Failures URL: https://michaelbir.es/fix/ai-app-breaks-in-production Published: 2026-07-06 Every rescued vibe-coded app I've seen failed for a variation of the same six problems. If your AI-built app works in the preview but falls apart with real users, triage it in this order. ## 1. Authentication that only worked in the sandbox Sessions drop, password resets 404, OAuth loops back to the login page. Usually: redirect URLs still pointing at the dev/preview domain, cookies misconfigured for the production domain, or the AI mixed two auth patterns that fight each other. Google sign-in redirecting to `localhost:3000` is the most common variant ([full fix](/fix/lovable-google-login-not-working/)); missing confirmation emails are usually SMTP, not frontend code ([email guide](/fix/lovable-email-not-sending/)). **Triage:** open the browser dev-tools Network tab during login. The first failing redirect or 401 tells you which layer is lying. ## 2. Database security that doesn't exist The app "works" because every table is publicly readable and writable — row-level security is off. This is the silent one: nothing looks broken until your data leaks. Roughly 70% of Lovable apps ship this way ([how to check and fix](/fix/supabase-rls-not-working/)). **Triage:** Supabase Table Editor → look for "RLS disabled" badges. This one is urgent even though nothing "looks" broken. ## 3. Payments that succeed without fulfilling Stripe takes the money; your app never finds out. Webhook endpoint on the wrong URL, wrong signing secret, or a parsed request body breaking signature verification ([the five causes](/fix/stripe-webhook-not-firing/)). **Triage:** Stripe Dashboard → Webhooks → recent deliveries. The response codes tell the story. ## 4. Environment & CORS chaos Works locally because `.env` had everything; production has half of it. Symptoms: blank pages, "undefined is not a function" in the console, API calls blocked by CORS, or — worse — secret keys shipped into the frontend bundle because the AI didn't know the difference. **Triage:** compare local `.env` against the hosting provider's environment variables line by line, then check the browser console for CORS errors and grep the built JS for `sk_live` / `service_role`. ## 5. Deploys that fail or half-succeed Build passes but the page is blank; custom domain shows an old version; every deploy needs a manual ritual someone has to remember. There's no CI/CD, no rollback, no monitoring — the AI built an app, not an operation. **Triage:** can you answer "what exactly runs in production right now, and how do we get back to the previous version?" If not, this is your problem even when nothing is currently on fire. ## 6. The AI is going in circles The meta-failure: every prompt fixes one symptom and creates two more. This happens because the codebase has accumulated contradictory patterns — three ways of fetching data, two auth flows, dead code the AI keeps "fixing." Past a certain entropy point, prompting is negative progress. If the app broke after a Lovable update or a half-finished "security fix," start with the regression triage ([post-update guide](/fix/lovable-app-broke-after-update/)). **Triage:** if the last ten prompts haven't produced net progress, stop prompting. The codebase needs a human root-cause read, and possibly a scoped rebuild of the rotten parts. ## The honest way out These six failures compound — broken env vars cause webhook failures cause "fixes" that break auth. Fixing them one symptom at a time is how you got here. I do it the other way around: a fixed-price **$299 triage** reads the entire codebase, finds the root causes, and gives you a video walkthrough + written report with an honest rescue-vs-rebuild recommendation — in 48 hours, before you spend anything on fixes. --- # How to Deploy a Lovable App to Production (Without It Breaking) URL: https://michaelbir.es/fix/deploy-lovable-app-production Published: 2026-07-06 Lovable's **Publish** button gets you a live URL. It does not get you a production app. The gap between the two is where most Lovable projects die — and where paying customers hit errors your demo never showed. Here's the checklist I run when taking a Lovable app to real production. ## 1. Domain and URLs - Connect your custom domain (Project → Settings → Domains) and wait for DNS + SSL to go green. - Then update **every place that stored the old URL**: Supabase Auth → URL Configuration (Site URL + redirect URLs), OAuth providers (Google/GitHub callback URLs), and Stripe webhook endpoints. Auth "works on the lovable.app URL but not on my domain" is almost always a stale redirect URL ([Google login fix](/fix/lovable-google-login-not-working/)). ## 2. Environment variables and secrets Lovable manages Supabase keys for you, but anything else — Stripe keys, email API keys, third-party tokens — must be checked: - **No secrets in frontend code.** Anything in the React code ships to every visitor. Search for `sk_live`, `service_role`, and API keys. - Secret keys belong in **Supabase Edge Function secrets** (or your backend host's env vars), never in the client. - Test keys (`sk_test_...`) must be swapped for live keys — in the server environment, not the code. ## 3. Supabase production configuration This is the big one. Before real users arrive: - **Row-level security on every table, with policies.** ~70% of Lovable apps ship with RLS disabled ([full guide here](/fix/supabase-rls-not-working/)) — that's your entire database public. - **Auth settings:** disable auto-confirm if you don't want fake signups; configure a real SMTP provider (the built-in email service is rate-limited and lands in spam — [email setup guide](/fix/lovable-email-not-sending/)). - **Backups:** confirm point-in-time recovery or at minimum daily backups are enabled on your Supabase plan. ## 4. Payments - Stripe webhook endpoint pointing at the production domain, with the production signing secret ([common failures here](/fix/stripe-webhook-not-firing/)). - Run one real live-mode transaction end to end: pay, webhook received, order recorded, email sent, refund works. ## 5. The things nobody thinks about until they hurt - **Error visibility:** by default you'll never know production is broken until a customer emails you. Add error tracking (e.g. Sentry) — Lovable apps ship with none. - **SEO / indexability:** Lovable apps are client-side rendered; Google may see an empty page ([why, and the fix](/fix/vibe-coded-site-not-indexed-google/)). - **Rate limiting & abuse:** public forms and endpoints will be found by bots within days. - **A rollback plan:** know how to get back to the last working version before you need to. ## When the deploy itself keeps failing Build errors after connecting GitHub, blank white pages on the custom domain, infinite loading — these usually trace back to environment differences, not the code the AI wrote last. The fastest path is a root-cause diagnosis rather than another 50 prompts. That's my Hardening & Deploy package: environments, CI/CD, monitoring, OWASP + RLS hardening, and your app live on your domain — $1,799 fixed. Or start with the $299 triage to find out exactly what stands between your demo and production. --- # Lovable App Broke After an Update? How to Recover Without Burning Credits URL: https://michaelbir.es/fix/lovable-app-broke-after-update Published: 2026-07-06 It worked yesterday. Today the preview is blank, login fails, or a feature you shipped last week vanished. You didn't change much — maybe Lovable updated something, you connected GitHub, you asked for a "security fix," or you ran out of credits mid-change. You're not alone. **Post-update regressions** are one of the fastest-growing support themes for vibe-coded apps in 2026. ## The four usual causes ### 1. A half-finished AI change You asked for a fix. The AI edited six files, hit a credit limit, or you reverted one file manually. The codebase is now **inconsistent** — two auth patterns, old env vars, dead imports. Symptoms: build passes but page is blank; or one feature works and another 404s. **Triage:** Project → History in Lovable. Find the last version that worked. Note the timestamp. Don't prompt forward from a broken state — restore or compare. ### 2. Platform or dependency shift Lovable, Supabase, or a npm dependency updated. Something that compiled yesterday fails today. Common signs: new TypeScript errors in the build log, Supabase client API mismatch, or Edge Function runtime change. **Triage:** Read the **build error verbatim** — the first error line, not the last. That's usually the root cause; the rest is cascade. ### 3. "Security fix" that broke the app A common pattern: you ask to secure the database. The AI enables RLS with no policies, moves API keys around, or changes auth — the app shows empty data or won't log in. G2 reviews of Lovable explicitly mention security prompts that **alter layout and consume credits** without fixing the real issue. **Triage:** Supabase → Table Editor. If RLS just got enabled and lists are empty, that's your answer ([RLS guide](/fix/supabase-rls-not-working/)). ### 4. Environment / URL drift You added a custom domain, switched from preview to production, or cloned to GitHub. Redirect URLs, Stripe webhooks, and env vars still point at the old URL. The app "broke" when you went live — not because the code regressed, but because **the environment changed**. **Triage:** [Production deploy checklist](/fix/deploy-lovable-app-production/) — domain, Supabase URLs, Stripe, env vars line by line. ## What to do in the first hour (don't prompt yet) 1. **Write down what worked and when** — last known good date, last feature that worked, last URL that worked. 2. **Check Lovable build/deploy logs** — failed build vs successful deploy with broken runtime are different problems. 3. **Open browser DevTools → Console** on the broken page — screenshot the first red error. 4. **Check Supabase Auth logs and Stripe webhook deliveries** if login or payments broke. 5. **Stop after 3 failed prompts** — if each fix creates new errors, you're in a negative-progress loop. ## The credit-burn trap Every "try this" prompt costs credits. Regression debugging is the worst credit burn: you're undoing damage, not building features. Founders report burning a month's allowance in a single afternoon toggling between two broken states. **Better move:** freeze the codebase, document symptoms, get a human root-cause read. That's exactly what the $299 triage is for — full audit in 48 hours, video walkthrough, honest "restore vs fix vs rebuild" recommendation, credited toward fixes. ## Symptom → likely fix map | What broke | Check first | |------------|-------------| | Blank white page | Console errors; failed JS bundle; env vars missing in production | | Login stopped working | Supabase Site URL / redirect URLs ([Google login guide](/fix/lovable-google-login-not-working/)) | | Data disappeared | RLS enabled without policies | | Payments stopped | Stripe webhook URL still on preview domain ([webhook guide](/fix/stripe-webhook-not-firing/)) | | Emails stopped | SMTP / rate limits ([email guide](/fix/lovable-email-not-sending/)) | | Everything at once | Half-finished AI change — restore last good version, then audit | ## When the app can't be saved Sometimes the fastest recovery is: restore last known good from Lovable history or GitHub, redeploy, then **harden one layer at a time** with a human driving. If history is messy or GitHub sync diverged, the triage tells you whether rescue ($799) or a scoped rebuild is cheaper than another week of prompts. Tell me what broke, when, and what you already tried — [start with triage](/services/vibe-code-rescue) before you spend more credits. --- # Lovable Emails Not Sending? Fix Signup & Password Reset URL: https://michaelbir.es/fix/lovable-email-not-sending Published: 2026-07-06 Your auth flow "works" — until a real user signs up and never receives a confirmation email. Or they click "Forgot password" and nothing arrives. You check spam. Nothing. You ask Lovable to fix it. It changes the email template. Still nothing. This isn't a frontend bug. **Supabase sends the emails**, and the default setup is fine for demos, not for production. ## Why emails fail in Lovable apps 1. **Built-in Supabase email** — rate-limited (a handful per hour), shared infrastructure, aggressive spam filtering. Fine for testing; unreliable for customers. 2. **Site URL / redirect URLs wrong** — password reset links point at `localhost` or an old preview URL ([same fix as Google login](/fix/lovable-google-login-not-working/)). 3. **Auto-confirm enabled** — you never notice missing emails because test accounts skip verification; real users hit flows you never tested. 4. **No custom SMTP** — production apps need a real email provider (Resend, Postmark, SendGrid, AWS SES). ## Quick checks (2 minutes) 1. **Supabase Dashboard → Authentication → Users** — did the user actually get created? If yes, the app works; email delivery is the problem. 2. **Authentication → Logs** — look for email send failures or rate-limit errors. 3. **Authentication → URL Configuration** — Site URL must match your live domain. Reset links use this. 4. Try signup with your own email. Wait 5 minutes. Check spam **and** promotions tab. ## The real fix: custom SMTP ### Option A — Resend (most common for Lovable stacks) 1. Create a [Resend](https://resend.com) account and verify your domain (DNS records: SPF, DKIM). 2. Supabase Dashboard → **Project Settings → Authentication → SMTP Settings**. 3. Enable custom SMTP: | Field | Typical value | |-------|----------------| | Host | `smtp.resend.com` | | Port | `465` (SSL) | | Username | `resend` | | Password | Your Resend API key | | Sender email | `noreply@yourdomain.com` (must be on verified domain) | 4. **Authentication → Email Templates** — customize confirm signup and reset password templates. Ensure links use `{{ .ConfirmationURL }}` and `{{ .SiteURL }}` correctly. ### Option B — Other providers Postmark, SendGrid, and AWS SES work the same way: verify domain, add SMTP credentials in Supabase, set a real sender address on your domain. ## Auth settings to verify while you're there - **Enable email confirmations** if you want verified users — and test the full flow after enabling. - **Secure email change** — on for production. - **Password reset** — confirm redirect URL in the template matches your live Site URL. - **Rate limits** — Supabase Auth rate limits can block bursts of signups; check **Authentication → Rate Limits** if emails stop after a spike. ## "Emails worked yesterday" If nothing changed in your code but email stopped: - You hit the **built-in email quota** (resets slowly). - Your domain got **greylisted** because you sent from an unverified address. - Supabase **Site URL changed** when you connected a custom domain — reset links now point somewhere invalid. ## When to bring me in Email + auth + custom domain issues often stack: wrong Site URL, no SMTP, and a broken reset flow in the app. Fixing one in isolation wastes another week of prompts. My $299 triage maps every auth and email path (signup, confirm, reset, magic link), checks Supabase and your live domain, and gives you a fix plan — or I wire SMTP, templates, and redirects in the Priority Fixes package ($799). --- # Lovable Google Login Not Working? Fix the localhost Redirect URL: https://michaelbir.es/fix/lovable-google-login-not-working Published: 2026-07-06 You added "Sign in with Google." It works when you test locally. On your Lovable preview URL or custom domain, clicking the button sends you to `localhost:3000` — or loops forever, or lands on a blank page. This is the **#1 auth problem** I see in Lovable + Supabase apps. It's not your Google settings alone. Supabase is the middleman, and it's still pointing at the wrong place. ## Why this happens The OAuth flow goes: your app → Google → **Supabase** → back to your app. Supabase decides the final redirect using **Site URL** and **Redirect URLs** in the Auth settings. Lovable and local dev often leave these at `http://localhost:3000`. Google OAuth can succeed and Supabase still sends users to localhost. ## Fix it in 10 minutes ### 1. Supabase → Authentication → URL Configuration Update **Site URL** to your live URL — not localhost: - Lovable preview: `https://preview-xxxxx.lovable.app` (or your `.lovable.dev` preview) - Custom domain: `https://yourdomain.com` - Production: whichever URL users actually open in the browser Under **Redirect URLs**, add every URL users might land on after login: ``` https://preview-xxxxx.lovable.app/** https://yourdomain.com/** https://*.lovable.app/** ``` The `/**` wildcard matters — Supabase rejects redirects that aren't explicitly allowed. ### 2. Google Cloud Console → OAuth client Under **Authorized redirect URIs**, you need Supabase's callback — not your app URL: ``` https://YOUR-PROJECT-REF.supabase.co/auth/v1/callback ``` Under **Authorized JavaScript origins**, add your app domains: ``` https://preview-xxxxx.lovable.app https://yourdomain.com ``` Do **not** put `localhost` here for production testing unless you're still developing locally. ### 3. Lovable custom domain? Update everything again Connecting a custom domain is a common trigger: auth "worked on the lovable.app URL" and breaks on your domain. Every time the public URL changes, repeat step 1 for the new domain. Also update Stripe webhooks and any OAuth provider if you added more than Google ([production deploy checklist](/fix/deploy-lovable-app-production/)). ### 4. Test in an incognito window Cached sessions hide redirect bugs. Open a private window, go to your **production or preview URL** (not localhost), and try Google sign-in once. ## Still broken? Check these | Symptom | Likely cause | |---------|----------------| | Redirects to localhost | Site URL still `localhost:3000` in Supabase | | "redirect_uri_mismatch" | Google callback URI missing or wrong project ref | | Infinite redirect loop | Site URL and Redirect URLs disagree; or http vs https mismatch | | Login works but user is logged out on refresh | Cookie domain / session storage mismatch across subdomains | | Works on preview, fails on custom domain | Custom domain not added to Supabase Redirect URLs | Open browser DevTools → **Network** tab during login. The first failed redirect (302 to wrong host, or 400 from Supabase) tells you which layer is wrong. ## When to stop prompting the AI If you've updated Supabase and Google three times and it still redirects to localhost, the codebase may have a hardcoded `redirectTo: 'http://localhost:3000'` in the sign-in call. The AI will often "fix" OAuth by changing the wrong file and burning credits. That's a $299 triage job: I trace the actual `signInWithOAuth` call, align every URL across Supabase, Google, and your deployed domain, and verify login holds in production — usually same day. --- # Stripe Webhooks Not Firing After Deploy? Here's How to Fix It URL: https://michaelbir.es/fix/stripe-webhook-not-firing Published: 2026-07-06 Your checkout works. Stripe shows the payment as succeeded. But the order never completes — no confirmation email, no subscription activated, no database record. Locally, everything worked. This is the single most common failure I see in AI-built (Lovable, Bolt, v0, Cursor) apps that just went live. The good news: it's almost always one of five causes. ## 1. The webhook endpoint points at the wrong URL Stripe doesn't know you deployed. If you created the webhook endpoint during development, it's probably still pointing at a `localhost` tunnel or a preview URL. **Fix:** In the [Stripe Dashboard → Developers → Webhooks](https://dashboard.stripe.com/webhooks), check the endpoint URL. It must be your production domain, e.g. `https://yourapp.com/api/stripe/webhook` — not `localhost`, not an `*.vercel.app` preview URL that changes on every deploy. ## 2. You're using the wrong webhook signing secret Each webhook endpoint has its own signing secret (`whsec_...`). The secret from your local Stripe CLI (`stripe listen`) is **different** from the one for your production endpoint. If your deployed app still has the CLI secret, every event fails signature verification with a `400`. **Fix:** Copy the signing secret from the production endpoint in the Stripe Dashboard and set it as `STRIPE_WEBHOOK_SECRET` in your hosting provider's environment variables (e.g. Vercel → Project → Settings → Environment Variables). Then **redeploy** — env var changes don't apply to already-built deployments. ## 3. Test mode vs. live mode mismatch Test-mode webhooks only receive test-mode events, and live-mode webhooks only receive live-mode events. AI-generated code frequently ships with the test key (`sk_test_...`) in production. **Fix:** Make sure all three match the same mode: your secret key (`sk_live_...`), your publishable key, and the webhook endpoint (created while the dashboard was in live mode). ## 4. The request body is being parsed before verification Stripe signs the **raw** request body. If your framework parses the body into JSON before the signature check, verification fails every time. This bites Next.js, Express (`express.json()`), and most AI-generated handlers. **Fix (Next.js App Router):** read the raw text, then verify: ```ts export async function POST(req: Request) { const body = await req.text(); // raw body — do NOT use req.json() const sig = req.headers.get("stripe-signature")!; const event = stripe.webhooks.constructEvent( body, sig, process.env.STRIPE_WEBHOOK_SECRET! ); // handle event… return new Response(null, { status: 200 }); } ``` ## 5. The handler returns an error, so Stripe retries — then gives up If your handler throws (missing env var, database not reachable from production, RLS blocking the insert), Stripe records failed deliveries and eventually disables the endpoint. **Fix:** Open the endpoint in the Stripe Dashboard and look at the **recent deliveries** log — it shows the exact HTTP status and response body your server returned. That error message is usually the real root cause (often a Supabase insert failing because row-level security is enabled but no policy allows the service role — see my [Supabase RLS guide](/fix/supabase-rls-not-working/)). ## Still stuck? Webhook failures are rarely just webhook failures — they're usually a symptom of environment variables, database policies, or deploy configuration being broken at the same time. That's exactly what my fixed-price triage finds: a full audit of your app with a video walkthrough and prioritized fix list, in 48 hours, for $299. --- # Supabase RLS Not Working (or Disabled)? Fix It Before Someone Finds It URL: https://michaelbir.es/fix/supabase-rls-not-working Published: 2026-07-06 If your app was built with Lovable, Bolt, or another AI builder on top of Supabase, there's a very good chance your database is readable — possibly writable — by anyone on the internet right now. This isn't scaremongering. Research behind **CVE-2025-48757** (CVSS 8.26, High) found that roughly **70% of sampled Lovable-built apps had row-level security disabled entirely**, with 303 exposed endpoints confirmed across 170 projects — leaking everything from emails to payment data. The AI builds a working demo; it does not secure your tables. ## How to check if you're exposed (2 minutes) 1. Open your Supabase Dashboard → **Table Editor**. Any table with an "RLS disabled" warning badge is publicly accessible through your project's auto-generated REST API. 2. Confirm it from the outside — this is exactly what attackers do: ```bash curl "https://YOUR-PROJECT.supabase.co/rest/v1/users?select=*" \ -H "apikey: YOUR_ANON_KEY" ``` Your anon key is **not a secret** — it ships in your frontend JavaScript to every visitor. If that curl returns rows, so can anyone. ## Why "just enabling RLS" breaks your app Enabling RLS with no policies denies **everything** — and suddenly your app shows empty lists and silent failures. This is why the AI (or a previous developer) probably turned it off in the first place: the app "worked" without it. RLS needs to be enabled *and* given correct policies: ```sql -- Enable RLS on the table alter table public.orders enable row level security; -- Users can only see their own orders create policy "Users read own orders" on public.orders for select using (auth.uid() = user_id); -- Users can only insert orders for themselves create policy "Users insert own orders" on public.orders for insert with check (auth.uid() = user_id); ``` Every table needs this thinking applied: who may read which rows, who may write which rows, and what your server-side code (using the service role) does on behalf of users. ## The other landmine: the service role key The `service_role` key **bypasses RLS entirely**. It must only ever live in server-side environment variables. AI-generated code regularly embeds it in frontend code or commits it to the repo — at that point, RLS doesn't matter, the attacker has god mode. Search your codebase for `service_role` and rotate the key immediately if it appears anywhere a browser can see. ## The safe migration path 1. **Inventory** every table and what the frontend actually queries. 2. **Write policies first**, enable RLS table by table, testing each app flow as you go. 3. **Move privileged operations** (webhooks, admin actions) to server-side code using the service role — never the client. 4. **Verify from outside** with curl against the REST API using only the anon key. 5. **Rotate keys** if the service role key was ever exposed. ## When to bring me in If your app is live with real user data and RLS off, you have both a security emergency and a delicate migration — enabling policies wrong takes your app down. My $299 triage audits every table, policy, and key exposure in 48 hours and gives you a prioritized fix plan; the Priority Fixes package implements it without breaking your production app. --- # Why Google Can't See Your Vibe-Coded Site (And How to Fix It) URL: https://michaelbir.es/fix/vibe-coded-site-not-indexed-google Published: 2026-07-06 You launched, shared the link, and… you're invisible. Weeks later, Googling your own product name finds nothing. Your site isn't penalized — it's most likely **empty as far as crawlers are concerned**. ## Why this happens to AI-built sites Lovable, Bolt, v0-style builds are typically **client-side rendered (CSR)** React apps: the server sends a nearly empty HTML file plus a JavaScript bundle, and the browser builds the page. Humans never notice. Crawlers do: - **Google** *can* execute JavaScript, but rendering is deferred, budgeted, and unreliable — CSR sites index slower, partially, or with missing content and meta tags. - **AI crawlers (GPTBot, ClaudeBot, PerplexityBot) don't execute JavaScript at all.** As more buying research happens inside AI assistants, a CSR site simply doesn't exist to them. - **Social previews** (Open Graph) are read by scrapers that also don't run JS — that's why your links show no title or image on X/LinkedIn/Slack. ## Check what crawlers actually see (2 minutes) ```bash curl -s https://yoursite.com | grep -i "` and a script tag with none of your actual content, crawlers get an empty shell. Confirm with Google Search Console → URL Inspection → "View crawled page." ## The fix: render on the server The durable solution is **server-side rendering (SSR)** or static generation, so the HTML arrives with your content in it: 1. **Migrate to a framework that renders on the server** — usually Next.js, since it's React and most vibe-coded frontends port over largely intact. Marketing pages get statically generated (fast, fully indexable); dynamic pages render server-side. 2. **Per-page metadata**: real ``, meta description, Open Graph tags, canonical URLs — per page, not one global set. 3. **The basics that CSR builds always miss**: `sitemap.xml`, `robots.txt`, structured data (JSON-LD) for rich results. 4. **Prerendering services** (or Cloudflare Workers tricks) exist as a stopgap, but they add fragility — for a business site, SSR is the fix, not a workaround. ## What this means commercially Until this is fixed, every dollar you spend on content or ads leaks: Google half-sees you, AI assistants recommend competitors, and shared links look broken. It's usually a contained migration — the app logic stays, the rendering and metadata layer changes. This is one of my standard rescue jobs: I migrate the site to SSR, wire correct metadata, sitemap, and structured data, and verify indexability in Search Console. Start with the $299 triage and I'll tell you exactly how big (or small) the job is for your codebase. --- # Compromised WooCommerce Store Cleaned & Hardened in Under 24 Hours URL: https://michaelbir.es/case-studies/woocommerce-hack-cleanup Published: 2026-06-15 Outcome: 3 backdoors removed, entry point closed, back online clean in < 24h _Client details anonymized. Timeline and findings are representative of the engagement._ ## The situation The owner of a WooCommerce store got the worst kind of morning: customers reporting that the site redirected them to a spam page, Google flagging the domain, and the store — their primary sales channel — effectively offline. A malware-scanner plugin had already "cleaned" the site once the week before. The infection came back within days. That detail was the most important clue: when a cleanup doesn't hold, the backdoor was never found. Scanners remove the visible payload; they don't find how the attacker gets back in. ## The response **Hour 0–2: Contain and preserve** Full snapshot of files and database for forensics. Admin passwords and salts rotated, suspicious admin sessions killed, and the database user password changed. The redirect payload was neutralized so customers were no longer being sent to spam pages while the investigation continued. **Hour 2–8: Find every way in — not just the obvious one** Manual audit of the codebase against clean WordPress/WooCommerce/plugin sources, including running `wp core verify-checksums`. Crawl of upload directories and full review of the database for injected content and users. Findings: - **Backdoor #1**: An injected file uploader disguised with a core-like filename inside `wp-includes` — the one the scanner plugin kept missing. - **Backdoor #2**: A modified legitimate plugin file executing attacker-supplied code via an innocuous-looking request parameter. - **Backdoor #3**: A rogue administrator account with a deliberately unremarkable name, created weeks earlier — persistence that survives any file cleanup. **Root cause**: An outdated plugin with a known vulnerability had let the attacker in the first time. Every "cleanup" that didn't patch it was an open invitation to return. **Hour 8–20: Clean rebuild of the infected layers** Core, theme, and plugins reinstalled from clean sources. Database swept for injected admin users, options, and scripts. The vulnerable plugin was updated. All secrets, API keys, and the database user password were rotated. **Hour 20–24: Harden and restore trust** File permissions locked down, PHP execution disabled in upload directories, server configuration reviewed, WAF rules and login protection added, and automated off-site backups configured. Review requests submitted to Google — the "this site may be hacked" warning cleared shortly after. ## The outcome - Three backdoors removed — including the two that automated scanning had missed twice. - The actual entry point closed, so the cleanup held. No reinfection since. - Store back online and clean in under 24 hours, blacklist warnings cleared, checkout verified end to end. - The owner walked away with a written incident report: how the attacker got in, everything that was found, and what now prevents a repeat. ## What this case teaches 1. **Reinfection means a missed backdoor.** If your site was "cleaned" and hacked again, someone treated the symptom. 2. **Attackers plant more than one way back in.** Files, database, and user accounts all need auditing. 3. **The cleanup is half the job.** Without patching the entry point and hardening, you're renting your site back from the attacker. --- If your store or site is showing the same symptoms, this exact engagement is my **Emergency Cleanup** service — **$349 fixed**, typically under 24 hours, with a **30-day reinfection guarantee**. --- # CVE-2025-48757: 170+ Lovable Apps With Open Supabase Tables URL: https://michaelbir.es/case-studies/lovable-supabase-rls-security-audit Published: 2026-05-29 Outcome: CVSS 8.26; 303 endpoints / 170 projects exposed in scan; payment_status bypass on Linkable This case study is **not a fictional client story**. It documents a real, searchable vulnerability disclosure — [CVE-2025-48757](https://nvd.nist.gov/vuln/detail/CVE-2025-48757) — and the rescue pattern I run when founders discover their Lovable app is in the same state. ## The documented incident **Discovered:** March 20, 2025 by Matt Palmer (Replit) while testing **Linkable** (`linkable.site`) — a Lovable-built marketing site for generating websites from LinkedIn profiles. **What he found:** By modifying a normal Supabase REST request from the browser (equivalent to `SELECT *`), unauthenticated visitors could read the full `users` table — emails and profile data for **500+ users** who had used the site. The issue was not a "leaked secret key"; Supabase's public `anon` key is designed to be client-side. The failure was **missing or insufficient Row Level Security (RLS)**. **Scope scan (March 21, 2025):** Palmer and Kody Low scanned projects listed on [Lovable Launched](https://launched.lovable.dev/) and found **303 vulnerable endpoints across 170 projects** (~10.3% of 1,645 analyzed) — exposing users, transactions, subscriptions, Gemini API keys, Google Maps tokens, and more. **Public confirmation:** On April 14, 2025, Palantir engineer Danial Asaria [publicly demonstrated](https://x.com/danialasaria/status/1911862269996118272) the same class of exploit on live Lovable apps. **CVE published:** May 29, 2025. CVSS v3.1 base score **8.26** (High). No vendor patch — mitigation is correct RLS configuration per table. ## The payment bypass follow-up (same app) In a May 24, 2025 re-test of Linkable (by then a paid product maintained by a Lovable employee), Palmer documented something worse than read access: by POSTing to `/rest/v1/website_generation` **without an auth header**, an attacker could insert records with `"payment_status": "paid"` — **bypassing Stripe entirely**. That write path persisted even after the initial disclosure and Lovable's April "security scan" release. ## Why this keeps happening to non-technical founders Lovable apps call Supabase **directly from the browser** using the anon key. Security depends entirely on RLS policies matching your business logic. The AI builds working demos; it does not reliably ship least-privilege policies. Lovable's scanner (released April 24, 2025) checks whether RLS **exists**, not whether it **works** — which researchers called a false sense of security. Nothing looks broken in the UI. Data leaks quietly until someone curls your REST API or reads the CVE. ## How I fix this pattern (my engagement) When a founder contacts me after reading about CVE-2025-48757 or noticing "RLS disabled" badges in Supabase, this is the playbook: 1. **External verification** — curl each table with only the anon key (same method attackers use). 2. **Table inventory** — map every table to actual frontend and Edge Function queries. 3. **Policy-by-policy RLS** — enable RLS one table at a time with correct `auth.uid()` checks so the app doesn't go blank. 4. **RPC and Edge Function audit** — lock down functions like `get_gemini_api_key` that appeared in the scan. 5. **Key rotation** if `service_role` ever appeared in client code. Typical engagement: **$299 triage** (48h audit + video walkthrough) → **$799 Priority Fixes** for the RLS migration without production downtime. ## What you should search to verify this yourself - `CVE-2025-48757` - `mattpalmer.io lovable RLS` - `linkable.site lovable vulnerability` - `lovable launched supabase exposed` ## Sources - [CVE-2025-48757 disclosure (Matt Palmer)](https://mattpalmer.io/posts/2025/05/CVE-2025-48757/) - [Statement on CVE-2025-48757 — scan methodology & Linkable timeline](https://mattpalmer.io/posts/2025/05/statement-on-CVE-2025-48757/) - [NVD entry](https://nvd.nist.gov/vuln/detail/CVE-2025-48757/) --- Worried your app is in the same state? [RLS fix guide](/fix/supabase-rls-not-working/) or [$299 security triage](/services/vibe-code-rescue). --- # Documented: Stripe Bypass on Linkable (CVE-2025-48757 Follow-Up) URL: https://michaelbir.es/case-studies/lovable-stripe-webhooks-saas Published: 2026-05-24 Outcome: Unauthorized POST set payment_status=paid; legitimate webhooks fail for same RLS/env reasons This case study covers a **verified public incident** from the CVE-2025-48757 disclosure — not a made-up founder narrative. The payment bypass on Linkable is the extreme version of a failure mode I see weekly: **money moves in Stripe, but the app database never updates correctly.** ## The documented incident: payment_status bypass **Context:** [Linkable](https://mattpalmer.io/posts/2025/05/statement-on-CVE-2025-48757/) (`linkable.site`) was a Lovable-built site — later a paid product actively maintained by a Lovable employee. **May 24, 2025 follow-up** by security researcher Matt Palmer: - The data endpoint had moved to `/rest/v1/website_generation`. - RLS appeared to work **only when an auth header was present**. - Removing the auth header switched the request to the **unauthenticated** security context — bypassing access controls entirely. - Palmer issued a **POST** with a crafted payload including `"payment_status": "paid"`. - The record was accepted. **Stripe was never involved.** This is cited in Palmer's statement with a sample payload (anonymized emails) and screenshot of the request. It demonstrates that broken RLS on payment-related tables is not theoretical — it was reproducible on a **first-party Lovable product** after the initial March disclosure and after Lovable shipped a "security scan" in April 2025. ## The everyday founder version (same root causes) Most founders don't discover this via security research. They discover it via support tickets: > "I paid in Stripe but my app still shows free tier." That legitimate failure — Stripe Checkout succeeds, app state unchanged — usually traces to one of: 1. **Webhook URL on preview/localhost** after deploy to production. 2. **Wrong webhook signing secret** in Supabase Edge Function env (`constructEvent` returns 400). 3. **RLS blocking the webhook handler's database insert** (handler uses anon key instead of service role). 4. **Missing event subscriptions** (`checkout.session.completed`, `customer.subscription.updated`). The [Lovable Stripe docs](https://docs.lovable.dev/integrations/stripe) describe the happy path; community threads ([r/lovable — Stripe subscriptions](https://www.reddit.com/r/lovable/comments/1kgnrpk/issues_with_stripe_subscriptions/), [payment integrations discussion](https://www.reddit.com/r/lovable/comments/1qvh4qy/we_need_to_talk_about_payment_integrations_in/)) document where it breaks in production. [Superblocks' analysis](https://www.superblocks.com/blog/lovable-vulnerabilities) of CVE-2025-48757 specifically notes: *"One of the most striking cases came from public Stripe integration endpoints. Attackers could override payment settings or inject unauthorized parameters."* ## How I fix this pattern (my engagement) **Triage first:** Stripe Dashboard → Webhooks → recent deliveries. The HTTP status and response body beat another AI prompt. **Typical fixes (Priority Fixes $799):** - Production webhook endpoint + live signing secret in server-side secrets. - Webhook handler on service role with idempotency on `event.id`. - RLS policies that allow server-side writes but not public reads on `subscriptions` / `orders`. - One live-mode test transaction end-to-end. For apps that may have been exposed (Linkable-class bugs): external anon-key curl test on payment tables before go-live. ## What you should search to verify - `CVE-2025-48757 linkable payment_status` - `matt palmer linkable stripe bypass` - `lovable stripe webhook not working reddit` ## Sources - [Statement on CVE-2025-48757 — Linkable data injection (Appendix A2)](https://mattpalmer.io/posts/2025/05/statement-on-CVE-2025-48757/) - [Lovable Stripe integration documentation](https://docs.lovable.dev/integrations/stripe) - [Lovable vulnerability explained (Superblocks)](https://www.superblocks.com/blog/lovable-vulnerabilities) --- Stripe shows paid but your app doesn't? [Webhook troubleshooting guide](/fix/stripe-webhook-not-firing/) or [contact me](/contact?service=vibe-payments). --- # Documented: Lovable Google Login Redirects to localhost:3000 URL: https://michaelbir.es/case-studies/lovable-google-login-custom-domain Published: 2026-03-25 Outcome: Root cause = Supabase Site URL still on localhost; fix published Mar 2025 This case study documents a **real, published troubleshooting story** — not an anonymized client engagement. The pattern appears repeatedly in Lovable community threads and vendor playbooks; it is one of the most common calls I get. ## The documented incident **Published:** March 25, 2025 by **Ishwar Rimal**, Senior Frontend Engineer at Intuit, on Medium. **His setup:** Lovable frontend + Supabase backend. He added "Sign in with Google." **What worked:** Cloning locally and testing on `localhost` — Google login succeeded. **What failed:** On the Lovable preview URL (`preview-xyz.lovable.dev`), clicking "Sign in with Google" **always redirected to `localhost:3000`**. **What he tried first:** Updated Google Cloud Console — authorized JavaScript origins and redirect URIs. Still broken. **Actual root cause (his words):** *"The issue wasn't with Google or my code; it was with Supabase."* The **Site URL** in Supabase Authentication → URL Configuration was still `http://localhost:3000`. After OAuth, Supabase sends users to Site URL — so every successful Google login landed on localhost regardless of Google settings. **His fix:** 1. Supabase → Authentication → URL Configuration → set **Site URL** to the Lovable preview URL. 2. Add production/custom domains to **Redirect URLs** when deploying. 3. Confirm Google Cloud has `https://<project>.supabase.co/auth/v1/callback`. The article has 6+ claps and remains a top search result for this exact error string. ## How common is this? Multiple independent sources describe the same failure mode: | Source | Claim | |--------|--------| | [Ishwar Rimal (Medium)](https://ishwar-rimal.medium.com/fixing-the-login-with-google-issue-in-lovable-with-supabase-integration-a005cc974ef9) | Preview works locally; preview URL redirects to localhost | | [RapidDevelopers OAuth guide](https://www.rapidevelopers.com/lovable-issues/resolving-redirect-issues-after-auth-flow-in-lovable) | "Most common mistake is Site URL still `http://localhost:3000`" | | [Afterbuild Labs ERR-145](https://afterbuildlabs.com/platforms/lovable-developer/problems/oauth-localhost) | "~15% of broken Lovable deployments" — three surfaces must align: Supabase Site URL, Google redirect URIs, client `redirectTo` | The failure is **silent**: users see Google consent, click Allow, then hit a dead localhost URL — no obvious error in the app console. ## Why custom domain makes it worse The same pattern triggers when founders connect a **custom domain**: auth "worked on lovable.app" and breaks on `yourdomain.com` because Supabase Redirect URLs and Site URL were never updated. That is the production variant of Rimal's preview-URL bug. ## How I fix this pattern (my engagement) When a founder describes "Google login sends me to localhost," I don't start with more Lovable prompts. I align all three surfaces in one pass: 1. Supabase Site URL + Redirect URLs (preview, production, custom domain with `/**` wildcards). 2. Google Cloud (and GitHub, if used) callback URIs. 3. Code audit for hardcoded `redirectTo: 'http://localhost:3000'` in shared auth utilities — the layer Ishwar Rimal's story implies but doesn't always show in generated code. Typical turnaround: same-day for a single-domain fix; **$799 Priority Fixes** when Stripe webhooks and env vars drifted at the same time. ## What you should search to verify - `lovable google login localhost supabase` - `ishwar rimal lovable google` - `supabase site url localhost lovable` ## Sources - [Fixing the "logIn with Google" issue in Lovable (Ishwar Rimal, Medium)](https://ishwar-rimal.medium.com/fixing-the-login-with-google-issue-in-lovable-with-supabase-integration-a005cc974ef9) - [Supabase Auth — Redirect URLs](https://supabase.com/docs/guides/auth/redirect-urls) - [Lovable OAuth redirecting to localhost (Afterbuild Labs)](https://afterbuildlabs.com/platforms/lovable-developer/problems/oauth-localhost) --- Same symptoms? [Step-by-step fix guide](/fix/lovable-google-login-not-working/) or [contact me](/contact?service=vibe-login). --- # Documented: Non-Technical PM Stuck in AI Debug Loops for 3 Months URL: https://michaelbir.es/case-studies/lovable-production-rescue-after-ai-loop Published: 2025-11-13 Outcome: Published playbook: stop after 3 loops, debug summary, sleep as a tool This case study is based on a **real published account** by a non-technical product manager — not a fictional "waitlist app founder." It explains why "the AI keeps fixing one thing and breaking another" is now a top search term, and why my audit-first model exists. ## The documented experience **Author:** [Andreea Papillon](https://medium.com/@andreeapapillon) — product leader, explicitly writing for *"solo founders and small teams without developer resources."* **Published:** November 13, 2025 — *"Debugging with AI: When It Goes in Circles — and How I Got Unstuck"* (Part 2 of a 3-part series on going from prompt to production). ### What she built Three months of building an app with AI tools (Warp agent, ChatGPT for specs). Real production target: App Store + web, Supabase auth, Google Cloud backend. ### What broke (her data) After 3 months she categorized her bugs: - **~90% backend** — auth worst (sessions, token issues, service account permissions). - **~10% frontend** — mostly vague requirements the AI filled with wrong assumptions. - **Auth bugs took 3–5× longer** to debug than anything else. ### The "black screen" loop (verbatim pattern) App worked locally. Sent to App Store Connect. TestFlight build: **black screen, no error.** She spent **days** in a loop with Warp and ChatGPT: > Agent: "Now the code should compile!" > Her: "Yes, we have done this several times. The build will compile but the UI won't load… then we remove the optional, it comp compile but the screen will be black. And so on." > Agent: "You're right — we've been going in circles." **Resolution:** She stopped, slept, came back with a **structured debug summary** the AI had written the night before, and fixed it in **20 minutes**. Root cause: a React Native configuration change broke rendering. ### Her published rules (directly relevant to rescue work) 1. **Stop after 3 loops** or 20 minutes — start a new conversation with a debug summary. 2. **Sleep is a debug tool** — AI gets context-confused after 10–15 exchanges. 3. **Mock data trap** — AI fakes API integrations so code "runs"; you think Stripe/Supabase is wired when it isn't. 4. **Environment variables** — "works on my machine" because `.env` loaded from wrong working directory. 5. **"Works locally, fails staging"** — redirect URLs, CORS, migrations not run in staging. ## Industry parallel This is not one person's quirk. The [Cursor community forum](https://forum.cursor.com/t/claude-sonnet-4-0-gets-stuck-in-loops/97598) documents Claude Sonnet repeatedly applying the same failed fix. Credit-based tools (Lovable, Bolt) **charge for each loop**, which turns negative-progress debugging into real money lost. ## How my service maps to this documented pattern Papillon's "debug summary + fresh eyes" is exactly what my **$299 triage** formalizes — except done by a senior engineer who reads the **whole repo**, not the last 15 chat messages: | Her self-serve playbook | My engagement | |-------------------------|---------------| | Write debug summary | Written report + recorded video walkthrough | | Stop after 3 AI loops | Audit before any more prompts or credits | | Compare to last working Git state | Root-cause read across auth, env, deploy | | "90% backend" | Priority Fixes on auth, RLS, webhooks — not UI symptoms | I don't claim I was her engineer. I claim this is the **documented failure mode** my service is designed for — and it's searchable. ## What you should search to verify - `andreea papillon debugging with AI goes in circles` - `cursor claude stuck in loops forum` - `vibe coding non technical founder backend bugs` ## Sources - [Debugging with AI: When It Goes in Circles (Andreea Papillon)](https://medium.com/@andreeapapillon/debugging-with-ai-when-it-goes-in-circles-and-how-to-get-unstuck-206248f8222c) - [From Product Manager to AI Builder (series)](https://medium.com/@andreeapapillon/from-product-manager-to-ai-builder-what-it-takes-to-go-from-prompt-to-production-62da32812ffe) - [Claude Sonnet 4.0 gets stuck in loops (Cursor forum)](https://forum.cursor.com/t/claude-sonnet-4-0-gets-stuck-in-loops/97598) --- Stuck in the same loop on a Lovable/Bolt app? [When to stop prompting](/fix/lovable-app-broke-after-update/) or [$299 triage](/contact?service=vibe-ai-loops). ---