# Beluga > Beluga is open-source software for building your own ecommerce site: a React 19 > storefront and admin, an Express 5 API, SQLite or Postgres, and Stripe Checkout for > payment. You fork it, deploy it yourself, and write code to make it yours. There is > no hosted Beluga. The whole documentation set is one Markdown file at https://belugajs.com/docs/all.md — drop it into a context window to answer questions against the real Beluga rather than a guessed one. The load-bearing rules are on the invariants page; they are written to be pasted verbatim into an assistant's instructions. ## Key pages - [What Beluga is (and is not)](https://belugajs.com/start/what-beluga-is/): A self-hosted, Stripe-backed store you fork and build on. Not a marketplace, not a hosted service, not a shipping platform. - [Quickstart](https://belugajs.com/start/quickstart/): From a clone to a running store with the demo catalogue, in a few minutes. - [Architecture](https://belugajs.com/start/architecture/): The directory layout, the shared contract, and the one seam that let the data source change without touching a component. - [The shape of a deployment](https://belugajs.com/deploying/shape/): One Node process serving API and static files, what must persist, and what breaks on a platform that forgets its disk. - [The seams](https://belugajs.com/building/seams/): Which files are yours to replace, which are designed to be extended in one direction, and which hold a rule that breaks silently when edited like the first kind. - [The API](https://belugajs.com/building/api/): The route surface, how authentication and CSRF work, the public store snapshot and its cap. - [Checkout](https://belugajs.com/money/checkout/): What happens between the cart and the confirmation page, and the three rules the code holds to. - [Build a receiver](https://belugajs.com/integrating/webhook-receiver/): Every payload, signature verification in Node and Python, the raw-body gotcha, delivery semantics, local testing, and a troubleshooting table. - [The invariants](https://belugajs.com/start/invariants/): The load-bearing rules. A change that breaks one is wrong even if every test passes. Written to be pasted into an assistant's instructions. - [Gotchas](https://belugajs.com/reference/gotchas/): Everything that will cost someone an hour, sorted by when it bites. Most have a longer explanation on their own page. ## Everything - [All pages, one file](https://belugajs.com/docs/all.md) - [What Beluga is (and is not)](https://belugajs.com/start/what-beluga-is/) - [Quickstart](https://belugajs.com/start/quickstart/) - [Architecture](https://belugajs.com/start/architecture/) - [Build your first store](https://belugajs.com/tutorials/first-store/) - [Deploy your store](https://belugajs.com/tutorials/deploy/) - [Go live](https://belugajs.com/tutorials/going-live/) - [Add a field to products](https://belugajs.com/tutorials/adding-a-field/) - [Restyle the storefront](https://belugajs.com/tutorials/restyle-the-storefront/) - [Connect a fulfilment webhook](https://belugajs.com/tutorials/fulfilment-webhook/) - [Sell a digital product](https://belugajs.com/tutorials/digital-product/) - [The shape of a deployment](https://belugajs.com/deploying/shape/) - [Environment reference](https://belugajs.com/deploying/environment/) - [Postgres](https://belugajs.com/deploying/postgres/) - [Backups and restore](https://belugajs.com/deploying/backups/) - [The seams](https://belugajs.com/building/seams/) - [Theming](https://belugajs.com/building/theming/) - [Adding a route](https://belugajs.com/building/adding-a-route/) - [The API](https://belugajs.com/building/api/) - [Conventions and contributing](https://belugajs.com/building/conventions/) - [Products, variants and options](https://belugajs.com/catalogue/products/) - [Digital products](https://belugajs.com/catalogue/digital-products/) - [Collections](https://belugajs.com/catalogue/collections/) - [Images](https://belugajs.com/catalogue/images/) - [Pages](https://belugajs.com/catalogue/pages/) - [Search and sort](https://belugajs.com/catalogue/search/) - [Catalogue CSV](https://belugajs.com/catalogue/csv/) - [Stripe setup](https://belugajs.com/money/stripe/) - [Checkout](https://belugajs.com/money/checkout/) - [Shipping](https://belugajs.com/money/shipping/) - [Tax](https://belugajs.com/money/tax/) - [Discount codes](https://belugajs.com/money/discounts/) - [Orders and fulfilment](https://belugajs.com/money/orders/) - [Refunds and restocking](https://belugajs.com/money/refunds/) - [Order CSV export](https://belugajs.com/money/order-export/) - [Staff accounts](https://belugajs.com/accounts/staff/) - [Customer accounts](https://belugajs.com/accounts/customers/) - [Abandoned cart recovery](https://belugajs.com/accounts/abandoned-carts/) - [Outbound webhooks](https://belugajs.com/integrating/webhooks/) - [Build a receiver](https://belugajs.com/integrating/webhook-receiver/) - [Stripe webhooks (inbound)](https://belugajs.com/integrating/stripe-webhooks/) - [Store settings](https://belugajs.com/operating/settings/) - [Storefront visibility](https://belugajs.com/operating/visibility/) - [Email](https://belugajs.com/operating/email/) - [SEO and link previews](https://belugajs.com/operating/seo/) - [Examples](https://belugajs.com/examples/) - [A custom product card](https://belugajs.com/examples/product-card/) - [A custom landing page](https://belugajs.com/examples/landing-page/) - [A webhook receiver](https://belugajs.com/examples/webhook-receiver/) - [A catalogue CSV](https://belugajs.com/examples/catalogue-csv/) - [A Dockerfile](https://belugajs.com/examples/dockerfile/) - [A reverse proxy](https://belugajs.com/examples/reverse-proxy/) - [Scripting the admin API](https://belugajs.com/examples/admin-api-script/) - [Roadmap and changelog](https://belugajs.com/reference/roadmap/) - [For AI assistants](https://belugajs.com/reference/ai/) - [The invariants](https://belugajs.com/start/invariants/) - [Gotchas](https://belugajs.com/reference/gotchas/) - [A CLAUDE.md for your fork](https://belugajs.com/examples/claude-md/) - [About beluga](https://belugajs.com/about/) ## The invariants, in brief These rules are the reason Beluga is shaped the way it is. They are not style. A change that violates one will usually compile, pass review, and be wrong in a way that shows up as money. Copy this list into your fork's `CLAUDE.md`, `AGENTS.md`, or whatever your assistant reads. :::note This page is written to be pasted into an assistant's context, not read start to finish. It's more useful to an agent than to a human skimming for an overview. ::: 1. **Money never comes from the request.** Prices and totals are read from the database on every path. A request carries product and variant ids and quantities only. The loop in `server/routes/checkout.ts` is where this is enforced. 2. **The webhook is the only thing that marks an order paid.** The success redirect proves nothing: a buyer can close the tab, and the URL can be visited directly. Never add a payment state transition anywhere else. 3. **Webhook handling is idempotent.** Events are deduplicated by id, and a failed handler releases the dedup record so Stripe's retry is actually processed rather than dismissed as a duplicate. See `recordWebhookEvent` and `forgetWebhookEvent` in `db/orders-repository.ts`. 4. **Every admin route is behind `requireAdmin` and `verifyCsrf`**, applied once to the whole router in `server/routes/admin.ts`. Never mount an admin endpoint outside that router, and register every new route in `server/security.test.ts`. 5. **Every schema change lands in both dialects.** `db/schema.sqlite.ts` and `db/schema.pg.ts` are edited together, then `npm run db:generate` emits a migration for each. Commit all of it. 6. **Products reach Stripe only through explicit publish** (`POST /api/admin/products/:id/publish`). Saving never writes to Stripe. 7. **Stripe Prices are immutable.** Changing an amount creates a new Price and archives the old one, which is why historic orders still resolve. Do not "fix" this by mutating.