Skip to content
Throttle Bird

Logbook · Launch notes

The ship-day checklist for a vibe-coded app

A checklist before you share the URL. Secrets, database rules, headers, HTTPS, rate limits, and the tags that make the link look finished.

2026-09-30 · Throttle Bird

The checks Throttle Bird runs, written out so you can do them by hand.

The model said the app is done when the happy path rendered. Launch is a different question: what can a stranger learn from the URL, and what does the link look like when someone pastes it into a chat. This is the list we run by hand, and the list Throttle Bird approximates with a passive scan. Work top to bottom. The first items are the ones that cost money or leak data. The last items are the ones that make the launch look accidental.

Secrets and the browser

  • Search the built client bundle, not just your editor. NEXT_PUBLIC_, VITE_, and string literals in client components are already public.
  • Stripe in the browser is pk_ only. sk_, rk_, and whsec_ stay on the server. Roll anything that already shipped.
  • OpenAI and Anthropic keys (sk-proj-, sk-ant-) are called from a route you control. The route knows who the user is.
  • No PEM private keys, no service-role JWTs, no sb_secret_ in client code.
  • .env files with real values are not in git. .env.example has empty placeholders.

Data rules

  • Supabase: RLS is on for every table in public. Policies name who the row belongs to. using (true) is a deliberate public table, or it is a bug.
  • The anon key may be in the client. Confirm with one limit=1 read that a private table does not answer it.
  • Firebase: realtime rules are not .read: true at the root. Firestore is not allow read, write: if true. The web apiKey can stay; restrict it by referrer anyway.
  • Admin SDKs and service keys are imported only from server files.

The HTTP response

  • The launch URL is HTTPS. http:// redirects to it. The certificate is not expiring this month.
  • These headers exist: Content-Security-Policy (even a strict starter with frame-ancestors 'none'), Strict-Transport-Security once HTTPS is solid, X-Content-Type-Options: nosniff, Referrer-Policy, and Permissions-Policy turning off camera and mic you do not use.
  • X-Frame-Options: DENY or CSP frame-ancestors. One of them is enough.
  • Cookies that are sessions are Secure, HttpOnly, and SameSite. A client-side token in localStorage is a trade you should be able to explain.

Abuse, cheaply

  • Any route that calls OpenAI or Anthropic, sends mail, or creates a Stripe object has a rate limit and returns 429 when it trips.
  • Auth routes have a limit even if the happy path is "just a demo". Demos get scripted.
  • File upload routes check type and size on the server. The client check is a convenience.
  • Error responses do not include stack traces, SQL, or env var names.

The link you are about to share

  • <title> is the product, not "Create Next App" or "Home".
  • Meta description is one or two real sentences.
  • Open Graph title, description, and a 1200×630 image. Paste the URL into a chat and look at the preview before your users do.
  • viewport is width=device-width, initial-scale=1. Then actually open it at a phone width. Sideways scroll is a launch bug.
  • <html lang> is set.
  • robots.txt exists and points at a sitemap. The sitemap lists the public pages, not /api and not preview junk.
  • A staging deploy is noindex. Production is not.

The boring operational bits

  • You can name the person who can roll a key at 1 a.m. If that person is you, the dashboards are logged in somewhere that is not the laptop you are about to close.
  • The domain you will say out loud is the canonical one. Apex or www, pick one, redirect the other.
  • A 404 page exists and does not throw.
  • If your app needs a real server to hold secrets, hosts like Railway let you set private variables in a dashboard (referral link: we may earn a cut, it costs you nothing).
  • You have a way to hear about exceptions that is not "a user tweets".
  • Analytics, if you add any, is something you can describe in a privacy note. A cookie banner for a tool you do not understand is not a privacy program.

How to use the list with an agent

Paste the section you are fixing, not the whole list. Agents given twenty tasks will half-finish all of them and mark the file done. One prompt, one change, then you look at the diff.

A scan is a way to regenerate the list from the live URL after the agent says it is finished. Run it against the deploy, not against localhost. Localhost cannot see the headers your host adds, and it cannot see the bundle your host actually serves.

None of this is a penetration test. It is the preflight that keeps the obvious stuff off the launch tweet. Do it before you announce the URL. Doing it the morning after also works. It is just a worse morning.

Check your own app

Paste your URL to run these checks automatically. Free, results in under a minute.

Scan my app free