The vibe-coded app production checklist.
You built something with Cursor, Lovable, Bolt, v0, or Replit, and it works. Before real customers and real data arrive, go through this list. Each item has a check you can run yourself, most of them in a few minutes.
Start with the first three sections. Missing database rules, leaked keys, and missing server-side permission checks cause the most damage and are the most common.
Database access rules
If your frontend talks to Supabase or Firebase directly, the key it uses is public. Your access rules are the only protection.
Row-level security is enabled on every table in an exposed schema.
Check: Supabase Security Advisor, or select tablename, rowsecurity from pg_tables where schemaname = 'public'.
Policies on private data are scoped to the user or team, never using (true).
Check: Read each policy and confirm it compares a column against auth.uid() or a membership table.
Roles and permissions don't come from user-editable metadata.
Check: Search policies and server code for user_metadata. Use app_metadata or a roles table instead.
Views respect the caller's permissions.
Check: Views over private tables are created with security_invoker = true, or are not exposed to the API.
Firebase and storage rules are not in test mode.
Check: Look for allow read, write: if true, expiring test-mode rules, or storage buckets marked public that hold private files.
Secrets and configuration
Anything bundled into client code is readable by every visitor. That includes environment variables with a public prefix.
No secret keys use a NEXT_PUBLIC_ or VITE_ prefix.
Check: Check .env files and your hosting provider's environment settings for service role, Stripe secret, or AI provider keys.
The production build contains no secret key formats.
Check: Build, then search the output for patterns like sk_live_, sk-proj-, sb_secret_, or AKIA.
Any key that was ever exposed has been rotated.
Check: Check Git history, not just current files. Moving a leaked key isn't enough; revoke it and issue a new one.
Development and production use separate keys and databases.
Check: A test run or a teammate's laptop should never be able to write to production data.
Input, uploads, and output
Treat everything that arrives from the browser as untrusted, including data your own frontend sends.
Request bodies are validated with a schema on the server.
Check: Zod, Valibot, or Pydantic at the start of each handler, rejecting unknown fields.
No database queries are built by joining strings.
Check: Search for raw SQL with template literals or + concatenation and switch to parameters.
User content rendered as HTML is sanitized.
Check: Search for dangerouslySetInnerHTML and v-html, and sanitize anything they render.
File uploads are limited by type and size, and stored privately.
Check: Try uploading an HTML file and a very large file. Neither should be served back from your domain.
Features that fetch a user-supplied URL can't reach internal addresses.
Check: Link previews and importers should block localhost, private IP ranges, and cloud metadata endpoints.
Payments and webhooks
Money logic should never depend on what the browser says.
Prices and amounts are looked up on the server.
Check: The browser sends a product or price ID only. Edit the request and confirm the charge doesn't change.
Webhook signatures are verified before acting.
Check: Stripe webhooks use constructEvent with the raw body. Send an unsigned request and confirm it's rejected.
Webhook handling is idempotent.
Check: Providers retry. Processing the same event twice should not grant access or credits twice.
Abuse and cost controls
Every endpoint that sends a message or calls a model costs money each time it runs.
Sign-up, login, password reset, and one-time codes are rate limited.
Check: Script 100 quick requests against each and confirm they start failing.
AI endpoints are rate limited per user.
Check: Limit by user ID, not just IP, and cap input length.
Hard spending limits are set with every paid provider.
Check: AI, SMS, and email providers all offer monthly caps or alerts. Set both.
Public forms have bot protection.
Check: Add a CAPTCHA such as Cloudflare Turnstile to sign-up and contact forms.
AI and LLM features
If your app uses a model, the prompt is an input users can influence, directly or through documents and web pages the model reads.
Nothing secret depends on the system prompt staying hidden.
Check: Assume users can extract it. Keep keys, internal URLs, and business rules out of it.
Tools the model can call run with the current user's permissions.
Check: A support bot should only be able to look up the signed-in user's orders, not query any order by ID.
Content the model retrieves is treated as untrusted.
Check: Emails, uploaded files, and web pages can contain instructions. Don't let them trigger actions without checks.
Model output is validated before it's used.
Check: Parse structured output with a schema, and never execute or render it as HTML unchecked.
Operations and recovery
Production readiness means knowing when something breaks and being able to recover when it does.
Errors are tracked and someone is alerted.
Check: Throw a test error in production and confirm a person gets notified.
Logs don't contain passwords, tokens, or full card or personal data.
Check: Search recent logs for a test user's email and token.
Backups are on, and a restore has actually been tested.
Check: Restore last night's backup to a separate project and confirm the app runs against it.
Security headers are set.
Check: At minimum HSTS, a Content-Security-Policy, and frame-ancestors or X-Frame-Options.
Dependencies are current and scanned in CI.
Check: Run npm audit or pnpm audit, and enable Dependabot or Renovate.
Critical flows have automated tests that run on every change.
Check: Sign-up, payment, permissions, and data deletion at minimum, so the next AI-generated change can't silently break them.
We'll run this list, and the things it can't catch, for you.
A checklist finds the common gaps. A security assessment also finds the logic flaws specific to your app, and our hardening work fixes them as reviewed pull requests with tests.
hello@shiplabs.appRun these checks only against applications you own or have written permission to test. This checklist covers common issues and is not a substitute for a full security review.
