playbooks / launch

The 10 Minute Pre Launch Check

Run this once before you share the link. It catches the mistakes that make the news.

10 minutes▲ severity if skipped: critical
The duck weighs in

duck@duckaudit

You want to ship today. Fine. Give me ten minutes first.

how it burns you

Most vibe coded apps get burned in the first week, not by genius hackers but by strangers pressing F12. The classics: a key in the bundle, an API route that never checks who is asking, a database that answers anyone. All three are findable in minutes, by you or by them.

1

Read your own bundle like an attacker

Everything in your frontend build is public. Search the built output for live keys before someone else does. If a secret shows up here, it is already leaked: rotate it, do not just delete the line.

bash
# Next.js: search the client bundle for the usual suspects
grep -rE "sk_live|service_role|AKIA|api_key" .next/static/ && echo LEAK || echo clean
2

Poke your API while logged out

Open a private browser window, sign out, and call your endpoints directly. Every route that changes data or reads private data must refuse you. If it answers politely, it will answer anyone.

bash
curl -i https://yourapp.com/api/admin/users
curl -i -X POST https://yourapp.com/api/orders -d '{"amount":1}'
# Expect 401 or 403. A 200 here is a breach with extra steps.
3

Knock on the database from outside

Your Supabase anon key ships to every visitor, so act like a visitor. Query your tables with only the anon key. Anything private that comes back means row level security is off or the policy is wrong.

ts
import { createClient } from "@supabase/supabase-js";
const anon = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
const { data } = await anon.from("users").select("*");
console.log(data); // this should be empty or an error, not your user list
4

Check what git remembers

The .env file being ignored today does not clean up the commit from last month. Search history for secrets. If you find one, treat it as leaked and rotate it, because scrapers read public git history all day.

bash
git log --all --diff-filter=A --name-only | grep -i env
git grep -E "sk_live|service_role" $(git rev-list --all) || echo history clean
5

Turn on the alarms

You cannot fix what you cannot see. Error tracking plus one uptime ping means you find out about weirdness from a dashboard, not from a user tweet. Both take five minutes on free tiers.

6

Rate limit the expensive routes

Anything that sends email, calls a paid AI API, or writes rows needs a speed limit. One angry script can otherwise turn your free weekend project into a four digit invoice.

ts
// simplest viable limiter for a route handler (per IP, in memory)
const hits = new Map<string, { n: number; t: number }>();
export function allow(ip: string, max = 20, windowMs = 60_000) {
  const now = Date.now();
  const h = hits.get(ip) ?? { n: 0, t: now };
  if (now - h.t > windowMs) { h.n = 0; h.t = now; }
  h.n += 1;
  hits.set(ip, h);
  return h.n <= max;
}

Make your AI do it

Paste this into Cursor or Claude Code from your project root.

Act as a security reviewer for this repo before launch. 1) List every place a secret could reach the client: scan for env vars without server only usage, keys in client components, and secrets in the built output. 2) List every API route and server action, and for each one tell me exactly where it verifies the caller's identity and authorization; flag any that do not. 3) If this project uses Supabase, list every table and whether row level security is enabled, and show the policies. 4) Point out routes with no rate limiting that call paid APIs or send email. Output a prioritized fix list with file paths. Do not change code yet.

The 10 minute check

0/6

receipts · every claim has a source

next playbook

Keys Stay Home