Deployment
Required services, customer-relevant environment variables, CORS, capacity planning, and production verification.
On this page
This page identifies customer-relevant deployment controls. Keep secrets server-side, terminate TLS on every public origin, and validate the final production origins—not just localhost.
Required services
A production deployment includes the NestJS API, background worker, Postgres, S3-compatible object storage, face/object-analysis sidecar, screen-analysis sidecar, and the customer dashboard. The candidate application hosts the Vue/raw SDK and Worker bundle and calls the public API/storage origins. When Vue runtime content is enabled, the same NestJS API serves public locale manifests from S3. S3 is the Content Studio system of record; resetting or rebuilding Postgres does not remove drafts, releases, locale manifests, production-channel pointers, or promotion history.
Post-session analysis and webhook delivery run in the background worker. Running only the HTTP API accepts sessions but leaves analysis/webhooks queued.
Customer-facing environment variables
| Variable | Purpose | Production guidance |
|---|---|---|
DATABASE_URL | Server/worker Postgres connection. | Use managed credentials and backups; never expose to browsers. |
SERVER_CORS_ORIGINS | Origins allowed to call the public API from browsers. | Exact comma-separated candidate/dashboard origins; blank fails closed in production. |
PUBLIC_SDK_RATE_LIMIT_PER_MINUTE | Public SDK request limit. | Capacity-test against event/media cadence. |
PUBLIC_SDK_EVENT_QUOTA_PER_MONTH / PUBLIC_SDK_MEDIA_QUOTA_MB_PER_MONTH | Per-organisation quotas; 0 disables. | Set explicit plan/trial limits and alerts. |
S3_BUCKET, S3_REGION, S3_ENDPOINT |
The full environment template is .env.example. Some variables configure internal administration, AI report enrichment, development/demo hosts, or optional notifications; do not expose those surfaces simply because a default exists.
Origins and CORS
There are two independent CORS boundaries:
- The API must allow the candidate page origin through
SERVER_CORS_ORIGINSand the application key's allowed-origin configuration. - Object storage must allow the exact origin through
S3_CORS_ORIGINSfor direct multipart requests.
Validate scheme, hostname, and port. Test both the ingest canary and recording-storage canary from the deployed candidate page.
The public content route intentionally does not use candidate cookies or a secret: published platform copy is public application data. Drafts, editor identities, release notes, audit records, and admin mutation endpoints remain behind the superadmin boundary. If content uses a separate origin, add that exact origin to the candidate application's CSP connect-src and configure API CORS for the candidate origin.
Content Studio durability and migration
Content Studio writes ETag-protected project/schema state, immutable state snapshots, and immutable release manifests under CONTENT_S3_PREFIX. The API performs all S3 access with its server-side IAM role; browsers never receive storage credentials. Grant the API role s3:GetObject and s3:PutObject for this prefix. The bucket-setup operator additionally needs s3:PutBucketVersioning.
Run pnpm s3:setup-bucket once per environment and verify bucket versioning is enabled. Protect the bucket with retention, replication, and deletion controls appropriate to the organisation's recovery objectives. Do not apply media-expiration rules to the Content Studio prefix.
On the first start after upgrading an existing deployment, the API checks S3 before serving Content Studio. If no S3 project exists, it imports the existing Postgres Content Studio rows—including drafts, releases, manifests, channels, and promotions—then makes S3 authoritative. Keep the old database available until this first start completes and verify that content-studio/v1/projects/proctoring-candidate/project.json exists. Subsequent empty or replaced databases do not trigger a catalog reset because the API loads Content Studio directly from S3.
Capacity and failure domains
Plan separately for:
- event request rate and database ingestion;
- recording bandwidth and object storage growth;
- worker concurrency and backlog age;
- face/object/screen analysis CPU and maximum video duration;
- webhook endpoint slowness and retry backlog;
- dashboard playback/transcoding;
- backups, restore tests, and media deletion.
Rate limits, quotas, request deadlines, and retry budgets are guard rails. They do not replace load testing. Run full-duration tests using the exact strict/standard policy mix expected in the trial.
Immutable application deployment and database persistence
Build application images outside the live environment and deploy an immutable image reference, preferably the exact source commit SHA or image digest. Pull and verify every required application image before draining workers or replacing running services. Keep runtime secrets in the deployment environment rather than baking them into images.
Build every image for the production host's native architecture. Check the Docker engine with docker info --format '{{.Architecture}}': aarch64/arm64 hosts require linux/arm64, while x86_64/amd64 hosts require linux/amd64. Do not rely on cross-architecture CPU emulation for production analysis workloads. Verify the platform recorded in each pushed image manifest before deployment.
Application replacement must not replace or delete the Postgres data volume. Pull only application images during an ordinary release; do not upgrade the Postgres image as an incidental side effect of an application deployment. Apply pending migrations before starting the new API and worker, and use backward-compatible expand/contract migrations because rolling an application image back does not reverse an already-applied database migration.
Create a database backup or managed-provider snapshot before applying pending migrations, retain independent scheduled off-host backups, and test restoration. Container image registries contain application artifacts only—never database contents, candidate evidence, credentials, or other runtime state.
Production verification
- Apply migrations before starting API/worker processes.
- Run the S3 bucket setup and confirm versioning is enabled before opening Content Studio.
- After the first S3 migration, restart against an empty non-production database and confirm Content Studio still shows the same drafts, releases, and production pointer.
- Load one published locale manifest and confirm its ETag changes only after promotion or rollback.
- Confirm API, worker, Postgres, storage, and sidecars are healthy.
- Resolve a fresh attempt from the production candidate origin.
- Pass preflight and confirm the chosen policy on the session.
- Verify ingest and storage canaries, continuous upload acknowledgements, and no CORS errors.
- End cleanly; play captured evidence and confirm analysis completes.
- Receive and verify a signed webhook; test a retry.
- Complete test-mode Checkout, confirm the subscription snapshot, open the Customer Portal, and reconcile one billable sitting without double-counting a reconnect.
- Delete a designated test session and confirm database/media removal.
- Confirm monitoring alerts on API errors, upload failures, queue backlog, analysis failure, and webhook exhaustion.
Proceed with the controlled-trial checklist only after these checks pass.