Skip to content

infra: stand up staging environment (separate Supabase + Telnyx + Vapi) #33

@ByteStreams-AI

Description

@ByteStreams-AI

Goal

Stand up a complete staging environment that mirrors production infra (Supabase, Vapi, Telnyx, Stripe, Cloudflare Workers) so all changes hit staging before customer-facing prod. Pairs with #31 (prod branch as deploy gate).

Motivation

The May 4–5 2026 voice-path incident chain (PRs #28#30) shipped speculative changes straight to the only cloud project we have. With staging, those changes break the test environment and never reach a real restaurant. This is the single highest-ROI mitigation against future regressions.

Topology after this lands

Branch Cloud target Domain Used for
feature branches (`feat/`, `fix/`, `chore/*`) none none local dev
`main` staging cloud `admin-staging.dialtone.menu` etc. live testing against real Vapi/Stripe/Telnyx in test mode
`prod` production cloud (current `klzznfagrtormretqsgb`) `admin.dialtone.menu` etc. real customer traffic

Sub-tasks (each can be its own commit; one PR for whole thing or split as makes sense)

A. Staging Supabase project

  • Create new Supabase project (e.g., `dialtone-staging`). Note the project ref + URL.
  • `pnpm supabase link --project-ref ` to link
  • `pnpm supabase db push --linked` to apply all migrations
  • Run `supabase/seed.sql` against staging (via SQL editor)
  • Set staging secrets via `pnpm supabase secrets set --project-ref `:
    • `STRIPE_SECRET_KEY` — Stripe test mode key
    • `STRIPE_WEBHOOK_SECRET` — different webhook endpoint per env
    • `TELNYX_API_KEY` — same key works, different sender number per env
    • `TELNYX_MESSAGING_PROFILE_ID` — staging-specific profile if applicable
    • `OPENAI_API_KEY` — same key fine
    • `DIALTONE_PUBLIC_BASE_URL` — `https://admin-staging.dialtone.menu\`

B. Telnyx staging number

  • Buy a separate phone number in Telnyx for staging (~$1/mo)
  • Configure SIP trunk pointing to Vapi (same IPs as prod number)
  • Note the number for Vapi binding

C. Vapi staging assistant + phone number

  • Vapi → Phone Numbers → import the staging Telnyx number
  • Create a new credential (BYOC SIP) for staging Telnyx if separate auth profile
  • Set the phone number's serverUrl to staging Supabase: `https://.supabase.co/functions/v1/vapi_call_start`
  • Test in Vapi: place a call, confirm assistant-request hits staging serverUrl

D. Stripe webhook (test mode) for staging

  • Stripe Workbench → switch to test mode → Webhooks → add endpoint `https://.supabase.co/functions/v1/stripe_webhook`
  • Subscribe to events: `checkout.session.completed`, `payment_intent.payment_failed`, `checkout.session.expired`
  • Copy the `whsec_*` signing secret → staging Supabase `STRIPE_WEBHOOK_SECRET`

E. Cloudflare Workers staging targets

  • Add staging targets in `apps/admin/wrangler.toml` and `apps/kitchen/wrangler.toml`:
    [env.staging]
    name = "dialtone-admin-staging"
    routes = [{ pattern = "admin-staging.dialtone.menu/*", zone_name = "dialtone.menu" }]
    vars = { VITE_SUPABASE_URL = "https://<staging-ref>.supabase.co", VITE_SUPABASE_ANON_KEY = "<staging-anon>" }
  • Bind `admin-staging.dialtone.menu` and `kitchen-staging.dialtone.menu` in Cloudflare Workers dashboard

F. GitHub Actions secrets

  • Add staging-specific secrets:
    • `STAGING_SUPABASE_URL`, `STAGING_SUPABASE_ANON_KEY`, `STAGING_SUPABASE_PROJECT_REF`, `STAGING_SUPABASE_ACCESS_TOKEN`
  • Rename existing prod-only secrets if needed for clarity (e.g. `PROD_*`)

G. Restructure `.github/workflows/deploy.yml`

Two design options — pick one:

Option 1 — single deploy.yml, branch-aware:

on:
  push:
    branches: [main, prod]
jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      ENV: \${{ github.ref == 'refs/heads/prod' && 'prod' || 'staging' }}
    steps:
      - …read STAGING_* or PROD_* secrets based on \$ENV
      - wrangler deploy --env \$ENV
      - supabase functions deploy --project-ref \$PROJECT_REF

Option 2 — two workflow files: `deploy-staging.yml` (on main) + `deploy-prod.yml` (on prod). Cleaner separation, more duplication. I'd lean Option 1.

H. Migration deploy workflow (folds in open follow-up #5)

  • Add `pnpm supabase db push --linked` step to the deploy workflow, gated on a label or a separate `migrate.yml` workflow_dispatch
  • Document migration push order: staging first, verify, then prod (after prod-branch merge)
  • Removes the manual `pnpm supabase db push --linked` step from the runbook

I. Environment-aware app config

  • Add a visual `STAGING` banner to `apps/admin` and `apps/kitchen` so you don't accidentally test prod data on staging or vice-versa. Read from `VITE_DIALTONE_ENV` env var or hostname.
  • Edge Functions check `Deno.env.get('SUPABASE_URL')` to detect env and adjust base URLs / log levels accordingly (already partial — `DIALTONE_PUBLIC_BASE_URL` env var)

J. Documentation

Cost summary

  • Supabase staging project: free tier sufficient for dev volume; $25/mo if Pro tier needed for advanced features
  • Telnyx staging number: ~$1/mo + per-message SMS costs
  • Stripe test mode: free
  • Vapi: same per-call rate, just routed to staging — call volume during testing is low
  • Cloudflare Workers: free tier covers both envs
  • Total ongoing: ~$1/mo + Vapi minutes during testing

Risks + things to think through

  1. Stripe test mode customers — staging restaurants need test payment methods (card 4242…). Document this.
  2. Telnyx 10DLC registration — staging messaging profile might need its own brand/campaign registration if testing real SMS deliverability. For dev, mock mode is fine (`TELNYX_MOCK_MODE` not set).
  3. Vapi assistant cross-pollination — make sure the staging Vapi phone number's serverUrl doesn't accidentally point at prod (and vice-versa). Easy to mis-set during a copy-paste.
  4. Greptile/CodeRabbit cost — extra reviews on staging PRs. Should be fine on PR-only basis.
  5. Schema drift — staging and prod can drift if a migration isn't pushed to both. The migrate.yml in sub-task H prevents this.
  6. Seed data parity — staging starts with seed.sql; prod starts with whatever real data accumulated. Don't test seed-resets in prod.

Suggested branch + commit chain

This is large enough that it could be split into 2-3 PRs:

```
git checkout -b chore/staging-supabase

Sub-tasks A + D (Supabase + Stripe webhook config — mostly external, doc the URLs)

git commit -am "chore: provision staging Supabase project + Stripe webhook"

git checkout -b chore/staging-vapi-telnyx

Sub-tasks B + C (Telnyx + Vapi staging numbers/config — external)

git commit -am "chore: provision staging Telnyx number + Vapi assistant"

git checkout -b chore/staging-deploy-workflow

Sub-tasks E + F + G + H (wrangler, GH Actions, deploy.yml, migrate.yml)

git commit -am "chore: split deploy workflow for staging vs prod with migration gate"

git checkout -b chore/staging-app-banner-and-docs

Sub-tasks I + J (banner + docs)

git commit -am "chore: env-aware staging banner + AGENTS.md release flow"
```

When this is done

  1. Push to `main` deploys to staging cloud only.
  2. Place a test call against the staging Telnyx number, verify happy path.
  3. Open `main → prod` PR, merge → deploys to prod cloud.
  4. Verified happy path on real Sui's number (`+16296001047`).

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions