Files

184 lines
7.9 KiB
YAML

# SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
# The three processes of this app, from one image (decision AT-D2, spec §13).
#
# Ordering (decision AT-D14, verified 2026-10-19 against conusai-deployer): the platform starts a
# deployment with a single `docker compose up -d --wait`, so `"kind": "job"` in conusai.json does
# not make the migrate service run first, and `pre_deploy_command` has no executor at all. What
# orders them is here: migrate carries `restart: "no"`, and web and worker wait for it with
# `condition: service_completed_successfully`. A failed migration therefore leaves the old
# containers running instead of starting a new release against an unmigrated schema.
#
# No service publishes a host port: the platform decides what is reachable from the
# `public` block of conusai.json. No service names a privileged option; the platform overlay adds
# `cap_drop: ALL`, `no-new-privileges` and the pid and log limits on top of what is here.
services:
web:
build:
context: .
dockerfile: Dockerfile
target: runtime
working_dir: /app/apps/web
# argv, never a shell string, and `next` directly rather than through a package manager: a
# package manager as PID 1 does not forward SIGTERM to the server it started.
command: ["node", "node_modules/next/dist/bin/next", "start"]
environment:
NODE_ENV: production
PORT: "3000"
CONUSAI_API_URL: ${CONUSAI_API_URL}
CONUSAI_API_TOKEN: ${CONUSAI_API_TOKEN}
CONUSAI_PROJECT_ID: ${CONUSAI_PROJECT_ID}
CONUSAI_PUBLIC_URL: ${CONUSAI_PUBLIC_URL}
CONUSAI_ORG_ID: ${CONUSAI_ORG_ID}
CONUSAI_ORG_SLUG: ${CONUSAI_ORG_SLUG:-}
CONUSAI_RELEASE: ${CONUSAI_RELEASE}
CONUSAI_ENVIRONMENT: ${CONUSAI_ENVIRONMENT:-}
CONUSAI_DSN: ${CONUSAI_DSN:-}
OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-}
OTEL_EXPORTER_OTLP_PROTOCOL: ${OTEL_EXPORTER_OTLP_PROTOCOL:-}
OTEL_EXPORTER_OTLP_HEADERS: ${OTEL_EXPORTER_OTLP_HEADERS:-}
OTEL_SERVICE_NAME: ${OTEL_SERVICE_NAME:-}
OTEL_SERVICE_VERSION: ${OTEL_SERVICE_VERSION:-}
AI_MODEL: ${AI_MODEL:-}
LOG_LEVEL: ${LOG_LEVEL:-}
# The support channel: the web mints widget sessions for it and the worker's account sweep erases by it. # kit:support
CONUSAI_SUPPORT_CHANNEL: ${CONUSAI_SUPPORT_CHANNEL:-} # kit:support
POSTGRES_URL: ${POSTGRES_URL}
POSTGRES_READER_URL: ${POSTGRES_READER_URL}
AUTH_SESSION_SECRET: ${AUTH_SESSION_SECRET}
OIDC_ISSUER: ${OIDC_ISSUER:-}
OIDC_WEB_CLIENT_ID: ${OIDC_WEB_CLIENT_ID:-}
OIDC_WEB_CLIENT_SECRET: ${OIDC_WEB_CLIENT_SECRET:-}
OIDC_AUDIENCE: ${OIDC_AUDIENCE:-}
OIDC_RESOURCE: ${OIDC_RESOURCE:-}
OIDC_INTROSPECTION_CLIENT_ID: ${OIDC_INTROSPECTION_CLIENT_ID:-}
OIDC_INTROSPECTION_CLIENT_SECRET: ${OIDC_INTROSPECTION_CLIENT_SECRET:-}
OIDC_RESOURCE_STATUS_URL: ${OIDC_RESOURCE_STATUS_URL:-}
OIDC_SCOPES: ${OIDC_SCOPES:-}
AUTH_PROVIDER_GENERATION: ${AUTH_PROVIDER_GENERATION:-}
AUTH_COOKIE_NAME: ${AUTH_COOKIE_NAME:-}
AUTH_SESSION_IDLE_SECONDS: ${AUTH_SESSION_IDLE_SECONDS:-}
AUTH_SESSION_ABSOLUTE_SECONDS: ${AUTH_SESSION_ABSOLUTE_SECONDS:-}
AUTH_PROVIDER: ${AUTH_PROVIDER:-}
AUTH_IDENTITY_NAMESPACE: ${AUTH_IDENTITY_NAMESPACE:-}
# kit:convex-better-auth:begin -- the Better Auth deployment (AUTH_PROVIDER=convex-better-auth)
BETTER_AUTH_CONVEX_URL: ${BETTER_AUTH_CONVEX_URL:-}
BETTER_AUTH_CONVEX_SITE_URL: ${BETTER_AUTH_CONVEX_SITE_URL:-}
BETTER_AUTH_CONVEX_ADMIN_KEY: ${BETTER_AUTH_CONVEX_ADMIN_KEY:-}
BETTER_AUTH_RELAY_INTERVAL_MS: ${BETTER_AUTH_RELAY_INTERVAL_MS:-}
# kit:convex-better-auth:end
expose: ["3000"]
depends_on:
migrate:
condition: service_completed_successfully
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/api/ready').then(r=>process.exit(r.ok?0:1),()=>process.exit(1))"]
interval: 10s
timeout: 5s
retries: 3
start_period: 20s
init: true
stop_grace_period: 20s
restart: unless-stopped
deploy:
resources:
limits:
cpus: "1.0"
memory: 768M
worker:
build:
context: .
dockerfile: Dockerfile
target: runtime
working_dir: /app
command: ["node", "apps/worker/dist/main.js"]
environment:
NODE_ENV: production
CONUSAI_API_URL: ${CONUSAI_API_URL}
CONUSAI_API_TOKEN: ${CONUSAI_API_TOKEN}
CONUSAI_PROJECT_ID: ${CONUSAI_PROJECT_ID}
CONUSAI_ORG_ID: ${CONUSAI_ORG_ID}
CONUSAI_ORG_SLUG: ${CONUSAI_ORG_SLUG:-}
CONUSAI_PUBLIC_URL: ${CONUSAI_PUBLIC_URL}
CONUSAI_RELEASE: ${CONUSAI_RELEASE}
CONUSAI_ENVIRONMENT: ${CONUSAI_ENVIRONMENT:-}
CONUSAI_DSN: ${CONUSAI_DSN:-}
OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-}
OTEL_EXPORTER_OTLP_PROTOCOL: ${OTEL_EXPORTER_OTLP_PROTOCOL:-}
OTEL_EXPORTER_OTLP_HEADERS: ${OTEL_EXPORTER_OTLP_HEADERS:-}
OTEL_SERVICE_NAME: ${OTEL_SERVICE_NAME:-}
OTEL_SERVICE_VERSION: ${OTEL_SERVICE_VERSION:-}
AI_MODEL: ${AI_MODEL:-}
LOG_LEVEL: ${LOG_LEVEL:-}
DOCUMENTS_EXCLUDED_PAGE_CLASSES: ${DOCUMENTS_EXCLUDED_PAGE_CLASSES:-}
CONUSAI_SUPPORT_CHANNEL: ${CONUSAI_SUPPORT_CHANNEL:-} # kit:support
# kit:convex-better-auth:begin -- the sweep ends a deleted person's Better Auth account
AUTH_PROVIDER: ${AUTH_PROVIDER:-}
AUTH_IDENTITY_NAMESPACE: ${AUTH_IDENTITY_NAMESPACE:-}
BETTER_AUTH_CONVEX_URL: ${BETTER_AUTH_CONVEX_URL:-}
BETTER_AUTH_CONVEX_ADMIN_KEY: ${BETTER_AUTH_CONVEX_ADMIN_KEY:-}
# kit:convex-better-auth:end
POSTGRES_URL: ${POSTGRES_URL}
POSTGRES_READER_URL: ${POSTGRES_READER_URL}
WORKER_HEARTBEAT_FILE: /tmp/worker-heartbeat
depends_on:
migrate:
condition: service_completed_successfully
healthcheck:
# The relay writes the file on every pass; two missed passes is a stuck worker.
test: ["CMD", "node", "apps/worker/dist/healthcheck.js"]
interval: 15s
timeout: 5s
retries: 3
start_period: 20s
init: true
stop_grace_period: 30s
restart: unless-stopped
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
migrate:
build:
context: .
dockerfile: Dockerfile
target: runtime
working_dir: /app
command: ["node", "apps/worker/dist/migrate.js"]
environment:
NODE_ENV: production
CONUSAI_API_URL: ${CONUSAI_API_URL}
CONUSAI_API_TOKEN: ${CONUSAI_API_TOKEN}
CONUSAI_PROJECT_ID: ${CONUSAI_PROJECT_ID}
CONUSAI_RELEASE: ${CONUSAI_RELEASE}
CONUSAI_ENVIRONMENT: ${CONUSAI_ENVIRONMENT:-}
CONUSAI_DSN: ${CONUSAI_DSN:-}
OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-}
OTEL_EXPORTER_OTLP_PROTOCOL: ${OTEL_EXPORTER_OTLP_PROTOCOL:-}
OTEL_EXPORTER_OTLP_HEADERS: ${OTEL_EXPORTER_OTLP_HEADERS:-}
OTEL_SERVICE_NAME: ${OTEL_SERVICE_NAME:-}
OTEL_SERVICE_VERSION: ${OTEL_SERVICE_VERSION:-}
POSTGRES_URL: ${POSTGRES_URL}
POSTGRES_READER_URL: ${POSTGRES_READER_URL}
# The owner's URL. The platform sends it to this service alone, because this is the service
# whose conusai.json entry declares postgres.migrate (data-services DS21, decision AT-D20).
POSTGRES_MIGRATE_URL: ${POSTGRES_MIGRATE_URL}
# kit:convex-better-auth:begin -- the migrate job deploys the Better Auth functions, and alone holds the secret
AUTH_PROVIDER: ${AUTH_PROVIDER:-}
CONUSAI_PUBLIC_URL: ${CONUSAI_PUBLIC_URL}
BETTER_AUTH_CONVEX_URL: ${BETTER_AUTH_CONVEX_URL:-}
BETTER_AUTH_CONVEX_ADMIN_KEY: ${BETTER_AUTH_CONVEX_ADMIN_KEY:-}
BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET:-}
# kit:convex-better-auth:end
# A job runs once. Without this, compose restarts it and `service_completed_successfully`
# is never reached.
restart: "no"
init: true
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M