Contents
Part I. Getting started
Part II. Building
Part III. What actually breaks
Part IV. Shipping
Reference
The vibe coding glossary
The model uses these words without ever defining them, and asking feels like admitting something. Here they all are, with the part a normal definition leaves out: why it matters to you.
Anon key (public key)
The public identifier your frontend uses to talk to Supabase or Firebase. It is meant to be visible in your JavaScript.
Why it matters: Its being public is fine and intended, but only because the real protection is supposed to be your database rules. Public key plus open rules is not two small problems, it is one total one.
Read the chapter →API
A way for one program to talk to another without a human clicking anything.
Why it matters: Your app's API endpoints are reachable by anyone, not just by your buttons. Whatever you did not check on the server is unchecked.
Authentication (authn)
Proving who someone is. Logging in.
Why it matters: Constantly confused with the next entry. Authentication is the bouncer checking your ID at the door.
Bundle
The JavaScript file your site sends to every visitor's browser, containing your frontend code.
Why it matters: Everything in it is public. Not obscured, not compiled beyond reading. Public. This is where leaked API keys are found.
Read the chapter →Client-side / server-side
Client-side code runs on the visitor's computer. Server-side code runs on yours.
Why it matters: The single most important distinction in this manual. The visitor can read, edit, and skip anything client-side. Only server-side checks are real.
CORS
A browser rule about which websites may call your API from a page.
Why it matters: It stops other people's *websites* from calling your API in a user's browser. It does not stop a person with a terminal. CORS is not a security boundary, and treating it as one is a common and expensive mistake.
Environment variable
A configuration value kept outside your code, usually in a .env file.
Why it matters: A .env file is not a vault. It keeps values out of git; it does nothing to keep them out of the browser if the build copies them there.
Read the chapter →Hashing
Scrambling a value one-way, so it can be checked but never reversed.
Why it matters: Passwords must be hashed, never encrypted and never stored plainly. If your app can email a user their forgotten password, it is storing it wrong.
Hydration
The moment React takes over server-rendered HTML in the browser and makes it interactive.
Why it matters: Mostly a source of confusing 'hydration mismatch' errors. It means the server and the browser rendered different things, commonly because of a date, a random value, or something read from localStorage.
IDOR (Insecure Direct Object Reference)
Changing an ID in a URL to see something that is not yours. /orders/1042 → /orders/1041.
Why it matters: One of the most common real breaches, requiring no skill whatsoever. The fix is to fetch by ID *and* owner, never by ID alone.
Read the chapter →Idempotent
Doing it twice has the same effect as doing it once.
Why it matters: Your payment endpoint must be idempotent. If it is not, a double-click is a double charge, and users double-click constantly on slow connections.
JWT (JSON Web Token)
A signed token proving who a user is. Your auth provider issues it on login.
Why it matters: The signature is what makes it trustworthy: it cannot be forged without your secret. This is why a JWT is a real identity and a value in localStorage is not.
localStorage
A place to store data in the visitor's browser. Persists across visits.
Why it matters: Completely under the user's control. Fine for a theme preference. Catastrophic for identity, roles, or credits, because the user can simply type a new value.
Read the chapter →Middleware
Code that runs before your route handler, usually to check auth or redirect.
Why it matters: Genuinely useful, and frequently mistaken for complete protection. Middleware that guards a page does not guard the API route the page calls.
Migration
A recorded change to your database structure.
Why it matters: The place a new table gets created, and therefore the place RLS gets forgotten. Every migration that adds a table should add its policy in the same change.
Read the chapter →NEXT_PUBLIC_ (and VITE_, REACT_APP_)
A prefix that tells your build tool to copy a variable's value into the browser bundle.
Why it matters: Not a namespace. An instruction. It reads like organisation and behaves like publication, and it is responsible for more leaked keys than anything else in this list.
Read the chapter →ORM
A library that lets you query a database in your programming language rather than in SQL. Prisma, Drizzle.
Why it matters: Convenient, and it quietly protects you from most SQL injection. It does not protect you from asking for the wrong rows. An ORM will fetch someone else's data very efficiently.
Rate limiting
Capping how often one user or IP can call something.
Why it matters: The only thing standing between your AI feature and a stranger's overnight loop. Without it, your API key is an uncapped credit card pointed at the internet.
Read the chapter →RLS (Row Level Security)
Postgres and Supabase's rule system. Policies on each table decide which rows a given user may see or change.
Why it matters: If RLS is off, the table has no protection at all and is readable by anyone holding the anon key, which is published in your JavaScript. There is no permissive rule to spot; there is simply nothing there.
Read the chapter →Security rules (Firestore)
Firebase's equivalent of RLS: a rules file describing who may read and write what.
Why it matters: Ships in test mode, as “allow read, write: if true”, which makes your database public. It never errors, never warns, and works perfectly all through development.
Read the chapter →Service role key
Supabase's master key. It bypasses every RLS policy you have written.
Why it matters: The worst single value to leak in this entire manual. If it reaches the browser, your policies are decoration. Server-side only, always.
Read the chapter →Session
The server's memory that this particular visitor is logged in.
Why it matters: A real session is a cookie or token your server verifies on every request. 'I set a variable saying they're logged in' is not a session.
SQL injection
Sneaking database commands into an input box, so they run as code.
Why it matters: Mostly solved if you use an ORM or parameterised queries, which the model usually does. It comes roaring back the moment anyone builds a query by pasting strings together.
SSR / SSG / CSR
Server-side rendering (built per request), static site generation (built once, at deploy), client-side rendering (built in the browser).
Why it matters: Mostly a speed and SEO concern. The security-relevant part: anything rendered in the browser had to be *sent* to the browser, including any data you fetched.
Vibe coding
Building software by describing it to an AI and iterating on the result, rather than writing most of the code by hand.
Why it matters: It genuinely works. What it does not do is transfer the judgment about what is correct, safe, and true from you to the model. That part is still yours.
Read the chapter →Webhook
A URL of yours that another service calls to tell you something happened. Stripe calls yours when a payment succeeds.
Why it matters: Anyone can call that URL. Verify the signature, or a stranger can POST 'payment succeeded' and get your product for free. This is a real and common bug.
When you’re ready to ship
Get an invite to PeakStack.
Knowing what RLS stands for and knowing whether yours is switched on are different kinds of knowledge, and only one of them protects anybody. PeakStack drives your live app in a real browser, checks what your landing page promises against what the app actually does, and runs the security pass this chapter describes: exposed keys, open database rules, client-side admin gates. You get a letter grade, ranked fixes, and an honest list of what we couldn’t check.
Private beta. Join the waitlist for an invite.