Skip to main content

Deployment

Navaid has multiple independently deployed units:

  1. frontend/ static SPA to Cloudflare Pages
  2. Postgres migrations/Auth configuration to Supabase
  3. checked-in Supabase Edge Functions
  4. optional agent/ LiveKit Agents worker
  5. optional R2 bucket/CORS and LiveKit/Resend/Sentry provider configuration
  6. docs/ Docusaurus site

This repository proves source and configuration intent. Verify each remote control plane before describing it as deployed.

Release Preconditions

  • branch/worktree scope reviewed
  • Node 20+ available
  • frontend lock valid under npm 10
  • frontend lint, unit tests, build, i18n audit pass
  • relevant Playwright personas pass
  • docs typecheck/build pass when docs change
  • migrations reset cleanly on local Postgres 17
  • Edge Functions tested with --no-verify-jwt
  • local HawkScan loop complete after application code changes
  • provider/secrets changes recorded without exposing values

Cloudflare Pages Frontend

Build settings

SettingValue
Root directoryfrontend
Node20 or later
Installnpm ci
Buildnpm run build
Outputfrontend/dist

npm run build runs tsc -b then Vite.

Dependency lock

Cloudflare uses npm 10. Any frontend dependency change must regenerate and validate the lock with:

cd frontend
npx -y npm@10 install --package-lock-only --no-audit --no-fund
npx -y npm@10 ci --dry-run --no-audit --no-fund

Confirm the @emnapi/core lock node exists before committing.

Frontend environment variables

VariableExposurePurpose
VITE_SUPABASE_URLBrowser publicSupabase project URL
VITE_SUPABASE_ANON_KEYBrowser publicRLS-constrained public key
VITE_APP_ENVBrowser publicEnvironment label
VITE_SENTRY_DSNBrowser publicSentry project ingestion DSN
SENTRY_ORGBuild onlySource-map upload
SENTRY_PROJECTBuild onlySource-map upload
SENTRY_AUTH_TOKENBuild secretSource-map upload authorization

Never configure VITE_SUPABASE_SERVICE_ROLE_KEY.

SPA routing

frontend/public/_redirects must be included in the publish output so client routes fall back to index.html. Verify direct navigation to:

  • /login
  • /invite?...
  • /shared/<token>
  • /oauth/consent?...
  • an authenticated /app/... route

Security headers

frontend/public/_headers deploys:

  • Content Security Policy
  • HSTS
  • frame denial
  • MIME sniffing protection
  • strict-origin referrer policy
  • permissions policy
  • cross-origin opener policy
  • immutable caching for hashed assets

The CSP currently permits Supabase HTTPS/WSS, Sentry ingest, Google Fonts, Cloudflare Insights, LiveKit HTTPS/WSS, blob workers/media, and required self/data origins.

Any new browser origin must be added deliberately. Test in report-only mode before weakening an enforcing policy.

Version and stale-client recovery

The Vite build emits dist/version.json with a build identifier. The browser:

  • polls it through UpdatePrompt,
  • handles vite:preloadError,
  • recognizes alternate lazy-chunk failure shapes,
  • performs at most one guarded reload.

After deployment, verify version.json is not served from a stale immutable cache while hashed assets are.

Sentry release assets

The build always creates hidden source maps:

  • with Sentry credentials, the Vite plugin uploads then deletes them;
  • without credentials, a fallback plugin deletes maps locally.

Inspect dist before publish. Source maps must not be publicly shipped.

Supabase Database

Local verification

npx supabase start
npx supabase db reset

This applies all 72 migrations and seed data to a disposable local database.

npx supabase link --project-ref bxmwvqnilignrxkitaae
npx supabase migration list

Linking stores project context. It does not apply migrations.

Apply migrations

npx supabase db push

Review the exact pending list first. Migrations can change or delete data and require the authority appropriate to the target environment.

Post-migration checks

Verify:

  • 31 expected tables
  • RLS enabled and intended grants/policies
  • SECURITY DEFINER execute grants
  • Before User Created function
  • pipeline guard triggers
  • Realtime publication for multiplayer tables
  • Storage avatars bucket/policies
  • system_config seeds and allowlist
  • current prompt rows
  • atomic usage/claim functions

Do not infer hosted Auth-hook activation from the SQL function existing.

Supabase Auth Control Plane

Configure and verify separately:

  • site URL
  • allowed redirect URLs
  • email OTP template and expiry/rate settings
  • production SMTP
  • Before User Created hook: pg-functions://postgres/public/hook_before_user_created
  • OAuth server and consent URL for MCP
  • dynamic client registration policy where required

Test:

  • unknown normal sign-in cannot create an account
  • valid code signup succeeds once
  • same-email retry is idempotent
  • pending invitation authorizes exact email
  • invalid/expired code fails
  • /oauth/consent round trip when MCP is enabled

Edge Functions

Source-of-truth rule

Edit supabase/functions/<name>/index.ts in the repository first. Do not deploy a dashboard-only source variant.

Required deployment command

Every function deploy uses:

npx supabase functions deploy <name> \
--project-ref bxmwvqnilignrxkitaae \
--no-verify-jwt

This includes ordinary HTTP functions, not only WebSockets and MCP.

Do not use a deployment tool that cannot pass --no-verify-jwt.

Deploy set

The current checked-in source names are listed in Edge Functions. Deploy only directories that exist and were intended for the release.

A local, Git-ignored supabase/config.toml may contain future placeholder names without source. It is not an instruction to deploy nonexistent functions.

Function secrets

Core AI

SecretRequired by
GEMINI_API_KEYGemini-spending functions
GEMINI_LIVE_MODELOptional Live model override
GEMINI_LIVE_VOICEOptional voice override
GEMINI_TTS_MODELOptional TTS model override

Trusted application mail

SecretRequired by
APP_URLsend-invite, notify-feedback, notify-session-add link origin
RESEND_API_KEYbranded application mail

APP_URL must be an absolute trusted HTTP(S) origin. Functions do not fall back to request-provided hosts.

LiveKit

LIVEKIT_URL
LIVEKIT_API_KEY
LIVEKIT_API_SECRET

Used by livekit-token and livekit-session-control.

Cloudflare R2

R2_ACCOUNT_ID
R2_ACCESS_KEY_ID
R2_SECRET_ACCESS_KEY
R2_BUCKET

The document store remains inert until all four exist.

Set secrets

npx supabase secrets set \
GEMINI_API_KEY=<value> \
APP_URL=https://app.example.com \
RESEND_API_KEY=<value> \
--project-ref bxmwvqnilignrxkitaae

Never put actual values in shell history shared with others, documentation, commits, screenshots, or tool output.

Post-deploy checks

For each changed function:

  • OPTIONS returns the canonical CORS headers
  • wrong method returns 405 where applicable
  • missing/invalid token returns generic 401
  • cross-tenant identifier returns 403/404
  • valid request reaches provider/data path
  • budget/rate errors are controlled
  • token usage changes for Gemini calls
  • logs contain no bearer/share/invitation tokens

For WebSockets, verify upgrade auth, reconnect and resume.

LiveKit Agent Deployment

The Node worker under agent/ must run on a persistent worker platform such as LiveKit Cloud Agents. It cannot be hosted as a request-scoped Supabase function.

Build/run:

cd agent
pnpm install
pnpm build
pnpm start

Agent secrets:

LIVEKIT_URL
LIVEKIT_API_KEY
LIVEKIT_API_SECRET
GOOGLE_API_KEY or GEMINI_API_KEY
SUPABASE_URL
SUPABASE_SERVICE_ROLE_KEY
GEMINI_LIVE_MODEL (optional)
GEMINI_LIVE_VOICE (optional)

The LiveKit project credentials must match the Edge Functions that mint/control room tokens.

Acceptance requires at least:

  • two users and the agent in one test room
  • correct per-speaker attribution
  • unauthorized/nonparticipant audio refusal
  • transcript persistence
  • maturity-safe session instructions
  • periodic token accounting
  • leave/remove disconnect from the media plane

The current worker notes that tighter named dispatch to session-* rooms is a future hardening item. Do not describe that hardening as already complete.

Cloudflare R2

Create a private S3-compatible bucket and credentials restricted to the intended bucket.

Browser direct PUT/GET requires R2 CORS compatible with:

  • production app origin
  • local development origin when needed
  • PUT, GET, HEAD
  • required content headers

Do not make the bucket public. Access is through five-minute signed URLs.

After configuration test:

  • status
  • opt-in disabled
  • upload and confirm
  • real-size cap
  • aggregate count/byte quota
  • member download
  • cross-tenant denial
  • admin delete and object removal
  • expired URL

Resend and Email

Verify sender domain and from/reply-to identities in Resend. Application mail and Auth OTP may use the same provider account, but configuration paths are separate.

Test localized invitation and notification templates without sending secret links to unintended recipients. Missing APP_URL must produce no link-bearing email.

Docusaurus Deployment

From docs/:

npm ci
npm run typecheck
npm run build

Publish docs/build using the documentation hosting configuration. Verify:

  • base URL and canonical URL
  • favicon/logo/social card
  • developer and user sidebars
  • broken-link build result
  • light/dark theme contrast
  • mobile navigation
  • search configuration if enabled

The docs site uses static Navaid branding; it does not read application theme_config.

CI/CD Reality

This repository currently checks in Dependabot configuration but no GitHub Actions deployment workflow. Do not document an illustrative workflow as if it is active.

If adding CI:

  1. pin Node 20 and npm 10 where frontend lock behavior matters,
  2. use least-privilege secrets/environments,
  3. run frontend lint/unit/build/i18n,
  4. run docs typecheck/build,
  5. run appropriate Playwright coverage,
  6. gate migration/function deployment separately from pull-request checks,
  7. require --no-verify-jwt for every function deploy,
  8. keep production DAST prohibited; use the local synthetic target,
  9. add manual approval/rollback for production mutation.

Monitoring

Sentry

The frontend already configures:

  • no default PII
  • browser tracing
  • 20% production trace sampling
  • 10% session replay and 100% replay on error
  • all replay text masked and media blocked
  • trace propagation to Supabase
  • React 19 error hooks and Sentry boundary

Validate telemetry after deployment without entering real confidential company content into a test error.

Supabase

Monitor:

  • Auth errors/rate limits
  • database CPU/connections/storage
  • slow RPC/policy queries
  • Edge Function errors/duration
  • Realtime connections
  • usage/accounting failures

Provider-specific

Monitor Gemini quota/errors, LiveKit room/agent health, R2 bytes/object count, Resend bounces, Sentry event/replay volume, and Cloudflare build/runtime metrics.

Rollback

Frontend

Roll back to a previous Cloudflare Pages build. Old/new clients must remain compatible with additive database changes.

Edge Functions

Redeploy the prior repository version using the same project ref and --no-verify-jwt. Model IDs with environment overrides can be reverted without source changes when designed for that purpose.

Database

Prefer forward corrective migrations. Do not edit/delete an already shared migration or use destructive reset commands against a remote environment.

Agent

Deploy/restart the previous worker version and confirm room dispatch/connection.

Secrets

Rotate compromised values rather than restoring known-compromised secrets. Follow docs/SECURITY_KEY_ROTATION_RUNBOOK.md.

Backup and Recovery

Do not claim backup schedules without verifying the Supabase plan/control plane. A recovery plan should cover:

  • database point-in-time/snapshot capability
  • provider secret inventory and rotation ownership
  • R2 object retention/lifecycle
  • application and docs build artifacts
  • migration/source commit mapping
  • restore test cadence

Backups are not proven until a restore has been tested.

Release Verification Checklist

  • exact commit/source state identified
  • migrations compared and applied intentionally
  • hosted Auth hook verified
  • affected functions deployed with required flags
  • provider secrets/status verified without disclosure
  • frontend build deployed with security headers and no source maps
  • version polling/chunk recovery checked
  • core sign-in/signup flow checked
  • one AI turn and accounting checked
  • cross-tenant negative check performed
  • optional R2/LiveKit/MCP path checked if changed
  • Sentry receives sanitized test telemetry
  • rollback target recorded

For recurring procedures, see Operations Runbooks.