Operations¶
Environments¶
| Environment | Notes |
|---|---|
| dev | Local pnpm workflow (pnpm dev). |
| production | Vercel project mypaidtraffic → brandvelia.com, app.brandvelia.com. |
| shared DB | Supabase project panoptes (São Paulo). Only the brandvelia schema is Brandvelia's. |
Build & quality gates¶
Run from the monorepo root:
pnpm install # install all workspace deps
pnpm build # production build (Turborepo → Next.js app)
pnpm lint # ESLint
pnpm typecheck # tsc --noEmit across all packages
pnpm test # Vitest (money path: INS decrypt, link builder, score, rate limit)
Gate: pnpm lint && pnpm typecheck && pnpm build && pnpm test must all pass before
a change is complete.
Database migrations¶
Migrations are versioned SQL files under supabase/migrations/ and are
additive-only on the shared brandvelia schema.
supabase db push --linked --dry-run # preview what will be applied
supabase db push --linked # apply (from repo root)
supabase migration list --linked # local vs remote state
supabase db dump --linked -f backup.sql # before structural migrations
Placeholders
*_shared_panoptes.sqlin the migrations directory are other systems' history applied to the shared database — do not edit or remove them.
Backup / disaster recovery (shared database)¶
panoptes is shared with other production systems, so any structural change requires a
verified safety net before the migration is applied:
- Confirm the Supabase project has daily backups / PITR enabled (Dashboard → Database → Backups). This is an operator action and a hard gate for structural migrations.
- Take a logical dump before structural changes:
supabase db dump --linked -f backup-$(date +%Y%m%d).sql. - Rollback policy: migrations are additive-only, so the rollback of a bad migration is a
new migration that disables/works around it — never
DROPagainst shared objects. - Restore drill: verify once per quarter that a backup actually restores (document the result here).
| Date | Drill | Result |
|---|---|---|
| — | — | pending first drill |
Deployment¶
Push to main (via PR) → Vercel deploys to production on brandvelia.com and
app.brandvelia.com. The Next.js app is the Vercel project root directory.
DNS / Vercel protection: *.vercel.app URLs require SSO; custom domains are public
(all_except_custom_domains).
Secrets (where each lives)¶
| Secret | Local | Vercel | Env var |
|---|---|---|---|
| Supabase anon key | .env.local |
encrypted, 3 stages | NEXT_PUBLIC_SUPABASE_ANON_KEY |
| Supabase service role | .env.local |
encrypted, 3 stages | SUPABASE_SERVICE_ROLE_KEY |
| OpenRouter key | .env.local |
sensitive, 3 stages | OPENROUTER_API_KEY |
| ClickBank INS secret | ~/.brandvelia/clickbank-ins-secret (600) |
3 stages | CLICKBANK_NOTIFICATION_SECRET |
| ClickBank API keys (Key 1 + 2) | ~/.brandvelia/clickbank-api.json (600) |
3 stages | CLICKBANK_API_KEY_1 / _2 |
| Cron guard token | .env.local |
3 stages | CRON_SECRET |
| Rate-limit store (Upstash) | .env.local |
3 stages | UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN |
| Marketplace feed URL (optional) | — | 3 stages | MARKETPLACE_FEED_URL |
Server-side secrets never reach client bundles. The ClickBank/INS secrets are also vendored (with a lifetime cap) but never committed to the repo.
Browser automation¶
Some ClickBank flows use a local Playwright browser:
npx playwright test # launches once, collects browsers, then exits
AI validation (E2E)¶
The Copilot pipeline is validated end-to-end: session → workspace → transactions → OpenRouter
call → response citing SQL-computed values → logged to bv_ai_runs (tokens, cost,
latency). Per-call cost is derived from the bv_ai_models registry.