Command Center (Operator)

Infrastructure & health

Environments, health endpoints, observability, scheduled jobs, and secrets hygiene.

Environments

The platform runs two separate environments, each from its own branch and its own database branch.

EnvironmentBranchApplication URLDatabaseEmail
Productionmainhttps://nia-lp-saas.vercel.appNeon productionReal email
UATdevelophttps://nia-lp-saas-uat.vercel.appNeon uatConsole only (no sending)

Merging a pull request into develop deploys UAT. Merging developmain deploys production.

These are genuinely separate. A change verified on UAT is not live for clients until the developmain release merge happens.

The other applications

ApplicationURLRepo path
Marketinghttps://nia-lp-marketing.vercel.appapps/marketing
Docshttps://nia-lp-docs.vercel.appapps/docs

Reachability of the platform, docs and marketing sites is checked by the production smoke script (tooling/scripts/src/prod-smoke.ts).

Infrastructure at a glance

ComponentDetail
DatabaseNeon Postgres, EU region, with pgvector and HNSW indices
StorageCloudflare R2 — avatars, templates, submissions, reports, policies
AIOpenRouter — embeddings plus chat completions, routed per task
Document parsingExternal parsing service for Word, PDF and image files
EmailResend
Function regionLondon, for low latency to the EU database

Database migrations

Migrations run automatically before every deployment build (apps/saas/scripts/migrate-on-deploy.mjs in the Vercel build command), so a schema change cannot reach an environment after the code that depends on it.

The migrate step fails the build if the build environment has no DATABASE_URL. It used to skip silently, which let a deployment look successful while the schema never moved — that is the failure this now refuses. Preview builds are exempt and still skip. When setting up an environment, confirm it carries DATABASE_URL before the first deploy, or that deploy will fail rather than quietly ship against an unmigrated schema.

Health endpoints

EndpointResponseUse for
GET /api/health200 OK textSimple liveness check
GET /api/health/live{"status":"up","checkedAt":"..."}JSON liveness
GET /api/health/ready200 if ready, 503 if notLoad-balancer and uptime monitoring
GET /api/health/sentry-testIntentional errorSentry integration testing (non-prod only)

The readiness endpoint probes Postgres connectivity, the pgvector extension, the ingestion job table, the object-storage endpoint, and whether the AI gateway key is configured. If any probe fails it returns 503 with a body naming the component that is down.

There is still no external uptime monitor watching these endpoints — that stays a deliberately parked decision. What does exist below: a daily check for cron-heartbeat staleness and email-delivery failure spikes that emails the operator once per new incident. It does not cover the platform going down out of hours generally — someone still has to look for that.

Alerts & email health

PageWhat it covers
Alerts (/operator/alerts)Cron-heartbeat staleness and email-failure-spike incidents, with state per incident
Email health (/operator/email)Sending-domain verification status and a 7-day failure count with the recent failures list

Observability

  • Request tracing: every API call carries an X-Request-ID header for log correlation.
  • Errors: captured to Sentry when SENTRY_DSN is set. Set both SENTRY_DSN (server) and NEXT_PUBLIC_SENTRY_DSN (client) for full coverage.
  • Logs: structured logs via @repo/logs, viewable in the Vercel dashboard under the deployment's logs.

Scheduled jobs

JobScheduleWhat it does
/api/cron/process-ingestionDaily, 03:00Drains the document ingestion queue
/api/cron/purge-conversationsDaily, 04:00Purges conversations past their retention window
/api/cron/kpi-mappingDaily, 05:00Runs scheduled KPI matching

Cron endpoints are protected by a shared secret header.

Backups

The database is copied to storage held away from the database provider by a job configured to run nightly; that job is not running on a schedule at present (copies exist for 1, 2 and 3 September 2026), and the provider's 6-hour point-in-time recovery window is the control for database restores. File backups are deleted 35 days after they are taken.

Live client data

Changes to live client data are only made through a written runbook with explicit, per-run human approval. Nothing is automated against production data.

Secrets hygiene

All secrets are Vercel environment variables and must never be committed.

SecretNotes
DATABASE_URLNeon connection string — strictly confidential
BETTER_AUTH_SECRETSession signing key — rotating it invalidates all active sessions
OPENROUTER_API_KEYHigh quota cost; revoke immediately if exposed
RESEND_API_KEYRevoke and regenerate if exposed
S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEYR2 credentials — restrict to required buckets
CRON_SECRETShared secret for the cron endpoints

Rotation cadence

SecretCadence
CRON_SECRETEvery 90 days
OPENROUTER_API_KEY, RESEND_API_KEYEvery 6 months or on exposure
S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEYEvery 6 months or on exposure
BETTER_AUTH_SECRETOnly if compromised
DATABASE_URL passwordOn exposure or annual audit

Practices

  • Never commit secrets. Audit the environment variable list periodically and remove unused keys.
  • Watch the AI gateway and email provider usage dashboards for anomalous spikes, which can indicate a stolen key.
  • Keep secret values out of log output.
  • Sign-in and sign-up rate limiting is provided by the auth layer; watch logs for brute-force patterns.

Daily check

  1. Hit /api/health/ready on production and confirm every component is green.
  2. Review Usage & cost for spend anomalies.
  3. Check Ingestion Health for failed jobs and retry or investigate.
  4. Check Alerts for any open cron-staleness or email-failure incident.
  5. Confirm the latest intended commit is actually deployed to the environment you think it is.

On this page