The stack joins NNACT Pro’s Docker network (
openfieldpro_internal, the default
PROXY_NETWORK) so the proxy can reach its containers by name. Service names are
prefixed with campaign- because Docker DNS aliases are shared across that
network — an unprefixed api would collide with NNACT Pro’s own API.
One-time setup
Behaviour
- Every push to
mainrunsci.yml(typecheck, tests, build, image build, OpenAPI freshness) and thendeploy-production.yml, which deploys only if CI is green and fails if the deployed HEAD does not contain the pushed commit. A docs-only change needs no new images:change-detect.mjsmaps it to no rebuilds. - The seed is the schema step. The API refuses to
synchronizein production (synchronize: config.env !== 'production'), and the seed refuses to run withoutALLOW_SCHEMA_PUSH=true, which the pipeline exports. On the first deploy (no marker indata/.deployed-sha) and whenever an API path changes, the pipeline runs the seed: it creates or alters tables to match the shipped entities and (re)writes the two demo accounts idempotently. - Change-aware builds.
scripts/change-detect.mjsmaps the changed file set to the images that must be rebuilt, so an api-only fix does not rebuild the web app. The marker atdata/.deployed-sharecords the last deployed commit. - Public verification is off by default (
VERIFY_PUBLIC=false). Once DNS resolves, setVERIFY_PUBLIC=trueinDME_CAMPAIGN_ENVso every deploy asserts both public URLs answer.
Operations
GET /api/health (public, unversioned). It reports
process liveness only — the database has its own healthcheck in the stack.
Security notes
.envis never copied into an image (.dockerignore), and CI only ever sends it to the VPS over SSH.CORS_ORIGINSis allow-listed to the web origin; the API never echoes an arbitraryOrigin.- Called once per generation, so an abuse of the demo accounts can burn tokens — the demo password is a fixed value by choice. Ship without seeding the demo accounts before exposing this publicly.
- The model default is
claude-sonnet-5-5becauseclaude-sonnet-4-5was deprecated on 2026-09-30 (retirement 2026-11-30). Pin a different model withANTHROPIC_MODEL.