Deploy
Run SupaNet locally and ship it.
SupaNet is a standard Vite + React app with Supabase Edge Functions. If you have deployed a Supabase project before, none of this will surprise you.
Local development
npm install # install deps
npm run dev # Vite dev server on http://localhost:5173
npm run build # typecheck (tsc -b) + vite build
npm run lint # eslint
npm run typecheck # tsc -b --noEmit
npm test # vitest (frontend unit tests)
npm run test:deno # deno test (edge-function tests)
npm run gen:types # regenerate database types from the linked projectAlways run npm run build before committing UI or logic changes - it typechecks
the whole app. Also run npm test (and npm run test:deno if you changed
edge functions). Add or update tests when you add or change logic like parsing,
validation, or calculations. Tests for frontend logic live next to the code as
src/**/*.test.ts(x); edge-function tests live in supabase/functions/tests/.
Environment and secrets
Two kinds of configuration matter, and keeping them separate is the whole game.
Front end (build-time, public):
| Variable | Notes |
|---|---|
VITE_SUPABASE_URL | Inlined into the bundle |
VITE_SUPABASE_ANON_KEY | Public by design - RLS protects the data |
These are read at build time, so they must exist before npm run build.
Edge function secrets (server-side only):
| Variable | Notes |
|---|---|
OPENROUTER_API_KEY | Required. Set with supabase secrets set ..., never commit it |
OPENROUTER_MODEL / OPENROUTER_EFFORT | Optional overrides |
OPENROUTER_SITE_URL / OPENROUTER_APP_NAME | Optional ranking headers |
Never put a secret in the front end. Only the anon key belongs there. The OpenRouter key is an edge-function secret and must stay server-side.
Database
The schema lives in supabase/migrations as sequentially-numbered SQL files
(0001_init.sql, 0002_skills.sql, …). They are the single source of truth: a
fresh project becomes a working backend with one supabase db push, and from
then on you never apply migrations by hand.
How new migrations go live
A GitHub Action (.github/workflows/deploy-migrations.yml) runs supabase db push
whenever a file under supabase/migrations/ lands on main. The CLI only applies
what's pending (the remote tracks applied versions), so merging a PR that adds a new
migration file applies exactly that file — no manual step, safe to re-run.
To set this up, add two repository secrets (Settings → Secrets and variables → Actions):
| Secret | What |
|---|---|
SUPABASE_ACCESS_TOKEN | A Supabase personal access token (Dashboard → Account → Access Tokens) — the same one the functions workflow uses. |
SUPABASE_DB_PASSWORD | Your project's database password (Dashboard → Project Settings → Database). db push connects straight to Postgres, so the token alone isn't enough. |
The project ref defaults in the workflow and is overridable with a repository
variable SUPABASE_PROJECT_REF.
Adding a migration
# 1. Create the next sequential file (keep numbers unique and contiguous).
# Write it idempotently where practical (create … if not exists, drop … if exists).
$EDITOR supabase/migrations/0040_my_change.sql
# 2. (Optional) try it locally / against your linked project before merging.
supabase db push
# 3. Refresh the typed client and open a PR.
npm run gen:typesMerging the PR to main triggers the Action, which applies it to the live database.
One rule: every migration filename must have a unique numeric prefix. Two files sharing a number (e.g. two
0032_*.sql) collide —db pushderives the version from the prefix and will refuse the push. Always use the next free number.
Fresh project notes
On a brand-new project the storage schema can lag a few seconds. If applying
the full migration fails on storage.buckets, apply the core tables first, then
the storage section.
If you change the schema locally: refresh the typed client with
npm run gen:types and re-check the Supabase security advisors.
Auth redirects
Confirmation and magic-link emails use the Supabase project's Site URL plus the Redirect URLs allowlist. Set these to your deployed origin, or links will point at localhost.
Automatic deployments
Pushing to main updates everything automatically:
- Tests: a GitHub Action (
.github/workflows/test.yml) runs lint, build, frontend tests, and edge-function tests on every PR and every push tomain. The feature bot's PRs get the same checks via.github/workflows/claude-feature.yml(GitHub doesn't fire normal PR workflows for bot PRs, so the feature workflow runs them inline). - Frontend: Railway rebuilds the app from the latest commit.
- Edge functions: a GitHub Action redeploys any function that changed.
- Database migrations: a separate Action applies any new migration files (see Database above).
After the one-time setup of secrets and environment variables, you never run
supabase commands by hand.
Workflow
The workflow is trunk-based: run npm run build, commit, and push to main;
hosting and migrations pick it up.
Production serve
npm run start # serve dist/ with SPA fallback