Deployment
Navaid has multiple independently deployed units:
frontend/static SPA to Cloudflare Pages- Postgres migrations/Auth configuration to Supabase
- checked-in Supabase Edge Functions
- optional
agent/LiveKit Agents worker - optional R2 bucket/CORS and LiveKit/Resend/Sentry provider configuration
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
| Setting | Value |
|---|---|
| Root directory | frontend |
| Node | 20 or later |
| Install | npm ci |
| Build | npm run build |
| Output | frontend/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
| Variable | Exposure | Purpose |
|---|---|---|
VITE_SUPABASE_URL | Browser public | Supabase project URL |
VITE_SUPABASE_ANON_KEY | Browser public | RLS-constrained public key |
VITE_APP_ENV | Browser public | Environment label |
VITE_SENTRY_DSN | Browser public | Sentry project ingestion DSN |
SENTRY_ORG | Build only | Source-map upload |
SENTRY_PROJECT | Build only | Source-map upload |
SENTRY_AUTH_TOKEN | Build secret | Source-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.
Link and inspect
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
avatarsbucket/policies system_configseeds 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/consentround 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
| Secret | Required by |
|---|---|
GEMINI_API_KEY | Gemini-spending functions |
GEMINI_LIVE_MODEL | Optional Live model override |
GEMINI_LIVE_VOICE | Optional voice override |
GEMINI_TTS_MODEL | Optional TTS model override |
Trusted application mail
| Secret | Required by |
|---|---|
APP_URL | send-invite, notify-feedback, notify-session-add link origin |
RESEND_API_KEY | branded 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:
- pin Node 20 and npm 10 where frontend lock behavior matters,
- use least-privilege secrets/environments,
- run frontend lint/unit/build/i18n,
- run docs typecheck/build,
- run appropriate Playwright coverage,
- gate migration/function deployment separately from pull-request checks,
- require
--no-verify-jwtfor every function deploy, - keep production DAST prohibited; use the local synthetic target,
- 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.