Files
ai-contract-platform/yuqei-infra/compose/docker-compose.production.yml
T

242 lines
14 KiB
YAML

services:
postgres:
image: ${POSTGRES_IMAGE:?POSTGRES_IMAGE must reference an immutable production image}
volumes:
- postgres-data:/var/lib/postgresql/data
postgres-init-databases:
image: ${POSTGRES_CLIENT_IMAGE:?POSTGRES_CLIENT_IMAGE must reference an immutable production image}
redis:
image: ${REDIS_IMAGE:?REDIS_IMAGE must reference an immutable production image}
command: ["redis-server", "--appendonly", "yes", "--maxmemory-policy", "noeviction"]
ai-redis:
image: ${REDIS_IMAGE:?REDIS_IMAGE must reference an immutable production image}
command: ["redis-server", "--appendonly", "yes", "--maxmemory-policy", "noeviction"]
minio:
image: ${MINIO_IMAGE:?MINIO_IMAGE must reference an immutable production image}
volumes:
- minio-data:/data
contract-api-migrate:
image: ${CONTRACT_API_IMAGE:?CONTRACT_API_IMAGE must reference an immutable production image}
build: !reset null
pull_policy: always
contract-api:
image: ${CONTRACT_API_IMAGE:?CONTRACT_API_IMAGE must reference an immutable production image}
build: !reset null
# The base developer compose publishes 8100. Production traffic must enter
# through the TLS edge only; remove the inherited host binding entirely.
ports: !reset []
pull_policy: always
# SMTP remains opt-in through docker-compose.production.smtp.yml so disabled mode has no secret-file dependency.
environment:
CONTRACT_API_AI_PLATFORM_BASE_URL: "https://${RC06_AI_INTERNAL_SERVER_NAME:?RC06_AI_INTERNAL_SERVER_NAME is required}:8443"
CONTRACT_API_AI_PLATFORM_RC06_PUBLICATION_KEY: ${RC06_AI_PLATFORM_PUBLICATION_SERVICE_KEY:?RC06_AI_PLATFORM_PUBLICATION_SERVICE_KEY is required}
CONTRACT_API_RC06_AI_PARTICIPANT_CA_FILE: /run/yuqei/rc06/ai-participant-ca.pem
CONTRACT_API_RC06_PUBLICATION_FACADE_ENABLED: "true"
CONTRACT_API_RC06_LOW_LEVEL_COORDINATOR_HTTP_ENABLED: "false"
CONTRACT_API_RC06_PUBLICATION_ENVIRONMENT_ID: ${RC06_PUBLICATION_ENVIRONMENT_ID:?RC06_PUBLICATION_ENVIRONMENT_ID is required}
CONTRACT_API_RC06_PUBLICATION_TENANT_ID: ${RC06_PUBLICATION_TENANT_ID:?RC06_PUBLICATION_TENANT_ID is required}
CONTRACT_API_RC06_PUBLICATION_BEARER_TOKEN_FILE: /run/secrets/rc06_facade_bearer_token
CONTRACT_API_RC06_FROZEN_CANDIDATE_COMMIT: ${RC06_FROZEN_CANDIDATE_COMMIT:?RC06_FROZEN_CANDIDATE_COMMIT is required}
CONTRACT_API_RC06_RELEASE_IMAGE_DIGEST: ${RC06_CONTRACT_RELEASE_IMAGE_DIGEST:?RC06_CONTRACT_RELEASE_IMAGE_DIGEST is required}
CONTRACT_API_RC06_AI_RELEASE_IMAGE_DIGEST: ${RC06_AI_RELEASE_IMAGE_DIGEST:?RC06_AI_RELEASE_IMAGE_DIGEST is required}
CONTRACT_API_RC06_APPROVAL_ROOT_PUBLIC_KEY_FILE: /run/yuqei/rc06/approval-root-public.pem
CONTRACT_API_RC06_APPROVAL_ROOT_PUBLIC_KEY_SHA256: ${RC06_APPROVAL_ROOT_PUBLIC_KEY_SHA256:?RC06_APPROVAL_ROOT_PUBLIC_KEY_SHA256 is required}
CONTRACT_API_RC06_APPROVAL_ROOT_KEY_ID: ${RC06_APPROVAL_ROOT_KEY_ID:?RC06_APPROVAL_ROOT_KEY_ID is required}
CONTRACT_API_RC06_APPROVAL_KEYRING_EPOCH: ${RC06_APPROVAL_KEYRING_EPOCH:?RC06_APPROVAL_KEYRING_EPOCH is required}
CONTRACT_API_RC06_APPLY_SERVICE_PUBLIC_KEY_FILE: /run/yuqei/rc06/apply-service-public.pem
CONTRACT_API_RC06_APPLY_SERVICE_PUBLIC_KEY_SHA256: ${RC06_APPLY_SERVICE_PUBLIC_KEY_SHA256:?RC06_APPLY_SERVICE_PUBLIC_KEY_SHA256 is required}
CONTRACT_API_RC06_LEGAL_REVIEW_KEYRING_SHA256: ${RC06_LEGAL_REVIEW_KEYRING_SHA256:?RC06_LEGAL_REVIEW_KEYRING_SHA256 is required}
CONTRACT_API_RC06_ONLINE_SIGNING_PROVIDER: ${RC06_ONLINE_SIGNING_PROVIDER:?RC06_ONLINE_SIGNING_PROVIDER is required}
CONTRACT_API_RC06_ONLINE_SIGNING_KEY_REFERENCE: ${RC06_ONLINE_SIGNING_KEY_REFERENCE:?RC06_ONLINE_SIGNING_KEY_REFERENCE is required}
CONTRACT_API_RC06_ONLINE_SIGNING_KEY_VERSION: ${RC06_ONLINE_SIGNING_KEY_VERSION:?RC06_ONLINE_SIGNING_KEY_VERSION is required}
CONTRACT_API_RC06_ONLINE_SIGNING_ALGORITHM: ${RC06_ONLINE_SIGNING_ALGORITHM:-RSA_PSS_SHA_256}
CONTRACT_API_RC06_ONLINE_SIGNING_PUBLIC_KEY_SPKI_SHA256: ${RC06_ONLINE_SIGNING_PUBLIC_KEY_SPKI_SHA256:?RC06_ONLINE_SIGNING_PUBLIC_KEY_SPKI_SHA256 is required}
CONTRACT_API_RELIABLE_EVIDENCE_SIGNING_BACKEND: kms_gateway
CONTRACT_API_RELIABLE_EVIDENCE_SIGNING_ENDPOINT: ${RC06_KMS_SIGNING_ENDPOINT:?RC06_KMS_SIGNING_ENDPOINT is required}
CONTRACT_API_RELIABLE_EVIDENCE_SIGNING_CREDENTIAL_FILE: /run/secrets/rc06_kms_credential
CONTRACT_API_RELIABLE_EVIDENCE_SIGNING_CA_FILE: /run/yuqei/rc06/kms-ca.pem
CONTRACT_API_PUBLIC_POLICY_CANDIDATE_DIR: /run/yuqei/public-policy/candidate
CONTRACT_API_PUBLIC_POLICY_LEGAL_APPROVAL_FILE: /run/yuqei/public-policy/named-policy-legal-approval.json
CONTRACT_API_PUBLIC_POLICY_TRUSTED_KEYRING_FILE: /run/yuqei/public-policy/policy-legal-keyring.trusted.json
CONTRACT_API_PUBLIC_POLICY_RELEASE_DIGEST: ${CONTRACT_API_PUBLIC_POLICY_RELEASE_DIGEST:?CONTRACT_API_PUBLIC_POLICY_RELEASE_DIGEST is required}
CONTRACT_API_PUBLIC_POLICY_EXECUTOR_ENABLED: ${CONTRACT_API_PUBLIC_POLICY_EXECUTOR_ENABLED:-false}
volumes:
- contract-files:/data/contract-files
- ${PUBLIC_POLICY_CANDIDATE_DIR_HOST:?PUBLIC_POLICY_CANDIDATE_DIR_HOST is required}:/run/yuqei/public-policy/candidate:ro
- ${PUBLIC_POLICY_LEGAL_APPROVAL_FILE_HOST:?PUBLIC_POLICY_LEGAL_APPROVAL_FILE_HOST is required}:/run/yuqei/public-policy/named-policy-legal-approval.json:ro
- ${PUBLIC_POLICY_TRUSTED_KEYRING_FILE_HOST:?PUBLIC_POLICY_TRUSTED_KEYRING_FILE_HOST is required}:/run/yuqei/public-policy/policy-legal-keyring.trusted.json:ro
- ${RC06_APPROVAL_ROOT_PUBLIC_KEY_FILE_HOST:?RC06_APPROVAL_ROOT_PUBLIC_KEY_FILE_HOST is required}:/run/yuqei/rc06/approval-root-public.pem:ro
- ${RC06_APPLY_SERVICE_PUBLIC_KEY_FILE_HOST:?RC06_APPLY_SERVICE_PUBLIC_KEY_FILE_HOST is required}:/run/yuqei/rc06/apply-service-public.pem:ro
- ${RC06_KMS_CA_FILE_HOST:?RC06_KMS_CA_FILE_HOST is required}:/run/yuqei/rc06/kms-ca.pem:ro
- ${BACKEND_TLS_TRUSTED_CA_FILE:?BACKEND_TLS_TRUSTED_CA_FILE is required}:/run/yuqei/rc06/ai-participant-ca.pem:ro
secrets:
- source: rc06_facade_bearer_token
target: rc06_facade_bearer_token
- source: rc06_kms_credential
target: rc06_kms_credential
healthcheck:
test: ["CMD", "python", "-c", "import json,urllib.request; d=json.load(urllib.request.urlopen('http://127.0.0.1:8100/api/v1/health',timeout=5)); assert d['status']=='ok' and d['config_status']!='error'"]
interval: 10s
timeout: 5s
retries: 6
contract-review-worker:
image: ${CONTRACT_API_IMAGE:?CONTRACT_API_IMAGE must reference an immutable production image}
build: !reset null
pull_policy: always
volumes:
- contract-files:/data/contract-files
contract-playbook-governance-worker:
image: ${CONTRACT_API_IMAGE:?CONTRACT_API_IMAGE must reference an immutable production image}
build: !reset null
pull_policy: always
ai-platform-api-migrate:
image: ${AI_PLATFORM_API_IMAGE:?AI_PLATFORM_API_IMAGE must reference an immutable production image}
build: !reset null
pull_policy: always
ai-platform-prompt-assets-import:
image: ${AI_PLATFORM_API_IMAGE:?AI_PLATFORM_API_IMAGE must reference an immutable production image}
build: !reset null
pull_policy: always
# Production startup validates the packaged assets without publishing them.
# An approved publication batch must invoke the importer separately with --apply.
command: ["python", "-m", "yuqei_ai_platform_api.packaged_prompt_assets_import"]
ai-platform-api:
image: ${AI_PLATFORM_API_IMAGE:?AI_PLATFORM_API_IMAGE must reference an immutable production image}
build: !reset null
# Keep participant/internal routes off the host network. Contract reaches
# this service only over the private Compose network.
ports: !reset []
pull_policy: always
environment:
AI_PLATFORM_SECRET_FILE_ROOT: /run/secrets
AI_PLATFORM_RC06_PUBLICATION_SHARED_SECRET: ${RC06_AI_PLATFORM_PUBLICATION_SERVICE_KEY:?RC06_AI_PLATFORM_PUBLICATION_SERVICE_KEY is required}
AI_PLATFORM_RC06_FROZEN_CANDIDATE_COMMIT: ${RC06_FROZEN_CANDIDATE_COMMIT:?RC06_FROZEN_CANDIDATE_COMMIT is required}
AI_PLATFORM_RC06_RELEASE_IMAGE_DIGEST: ${RC06_AI_RELEASE_IMAGE_DIGEST:?RC06_AI_RELEASE_IMAGE_DIGEST is required}
AI_PLATFORM_PROVIDER_LEGAL_ATTESTATION_SECRET: ""
AI_PLATFORM_PROVIDER_ACCEPTANCE_RECEIPT_FILE: /run/yuqei/provider-acceptance/receipt.json
AI_PLATFORM_PROVIDER_ACCEPTANCE_TRUSTED_KEYS_FILE: /run/yuqei/provider-acceptance/trusted-keys.json
AI_PLATFORM_PROVIDER_ACCEPTANCE_EXPECTED_RECEIPT_SHA256: ${AI_PLATFORM_PROVIDER_ACCEPTANCE_EXPECTED_RECEIPT_SHA256:?AI_PLATFORM_PROVIDER_ACCEPTANCE_EXPECTED_RECEIPT_SHA256 is required}
AI_PLATFORM_PROVIDER_ACCEPTANCE_RECEIPT_MAX_TTL_SECONDS: ${AI_PLATFORM_PROVIDER_ACCEPTANCE_RECEIPT_MAX_TTL_SECONDS:-604800}
AI_PLATFORM_PROVIDER_ACCEPTANCE_RECEIPT_CLOCK_SKEW_SECONDS: ${AI_PLATFORM_PROVIDER_ACCEPTANCE_RECEIPT_CLOCK_SKEW_SECONDS:-300}
AI_PLATFORM_PROVIDER_ACCEPTANCE_KILL_SWITCH: ${AI_PLATFORM_PROVIDER_ACCEPTANCE_KILL_SWITCH:-false}
AI_PLATFORM_RELEASE_ID: ${RELEASE_ID:?RELEASE_ID is required}
AI_PLATFORM_RELEASE_IMAGE_DIGEST: ${AI_PLATFORM_RELEASE_IMAGE_DIGEST:?AI_PLATFORM_RELEASE_IMAGE_DIGEST is required}
secrets:
- source: ai_provider_api_key
target: ai_provider_api_key
volumes:
- ${AI_PLATFORM_PROVIDER_ACCEPTANCE_RECEIPT_FILE_HOST:?AI_PLATFORM_PROVIDER_ACCEPTANCE_RECEIPT_FILE_HOST is required}:/run/yuqei/provider-acceptance/receipt.json:ro
- ${AI_PLATFORM_PROVIDER_ACCEPTANCE_TRUSTED_KEYS_FILE_HOST:?AI_PLATFORM_PROVIDER_ACCEPTANCE_TRUSTED_KEYS_FILE_HOST is required}:/run/yuqei/provider-acceptance/trusted-keys.json:ro
worker:
image: ${AI_WORKER_IMAGE:?AI_WORKER_IMAGE must reference an immutable production image}
build: !reset null
pull_policy: always
contract-web:
image: ${CONTRACT_WEB_IMAGE:?CONTRACT_WEB_IMAGE must reference an immutable production image}
build: !reset null
pull_policy: always
profiles: ["backend-hosted-web"]
edge:
image: ${NGINX_IMAGE:?NGINX_IMAGE must reference an immutable production image}
restart: unless-stopped
environment:
BACKEND_SERVER_NAME: ${BACKEND_SERVER_NAME:?BACKEND_SERVER_NAME is required}
RC06_PUBLICATION_TENANT_ID: ${RC06_PUBLICATION_TENANT_ID:?RC06_PUBLICATION_TENANT_ID is required}
RC06_AI_INTERNAL_SERVER_NAME: ${RC06_AI_INTERNAL_SERVER_NAME:?RC06_AI_INTERNAL_SERVER_NAME is required}
NGINX_ENVSUBST_OUTPUT_DIR: /etc/nginx/conf.d
ports:
- "${BACKEND_HTTP_BIND_ADDRESS:-0.0.0.0}:${BACKEND_HTTP_PORT:-80}:80"
- "${BACKEND_HTTPS_BIND_ADDRESS:-0.0.0.0}:${BACKEND_HTTPS_PORT:-443}:443"
volumes:
- ../nginx/templates:/etc/nginx/templates:ro
- ${BACKEND_TLS_CERT_DIR:?BACKEND_TLS_CERT_DIR is required}:/etc/nginx/tls:ro
- ${BACKEND_TLS_TRUSTED_CA_FILE:?BACKEND_TLS_TRUSTED_CA_FILE is required}:/etc/nginx/trusted-ca.pem:ro
extra_hosts:
- "${BACKEND_SERVER_NAME:?BACKEND_SERVER_NAME is required}:127.0.0.1"
- "${RC06_AI_INTERNAL_SERVER_NAME:?RC06_AI_INTERNAL_SERVER_NAME is required}:127.0.0.1"
networks:
default:
aliases:
- ${RC06_AI_INTERNAL_SERVER_NAME:?RC06_AI_INTERNAL_SERVER_NAME is required}
read_only: true
tmpfs:
- /var/cache/nginx
- /var/run
- /etc/nginx/conf.d
- /tmp
security_opt:
- no-new-privileges:true
healthcheck:
test: ["CMD-SHELL", "wget --ca-certificate=/etc/nginx/trusted-ca.pem -qO- https://$${BACKEND_SERVER_NAME}/healthz >/dev/null && wget --ca-certificate=/etc/nginx/trusted-ca.pem -qO- https://$${RC06_AI_INTERNAL_SERVER_NAME}:8443/healthz >/dev/null"]
interval: 15s
timeout: 5s
retries: 6
depends_on:
contract-api:
condition: service_healthy
ai-platform-api:
condition: service_healthy
contract-files-backup:
image: ${BACKUP_UTILITY_IMAGE:?BACKUP_UTILITY_IMAGE must reference an immutable production image}
profiles: ["ops"]
entrypoint: ["/bin/sh", "-c"]
command: ["exit 0"]
volumes:
- contract-files:/source:ro
- ${BACKUP_HOST_DIR:?BACKUP_HOST_DIR is required}:/backup
read_only: true
security_opt:
- no-new-privileges:true
minio-backup:
image: ${MINIO_MC_IMAGE:?MINIO_MC_IMAGE must reference an immutable production image}
profiles: ["ops"]
entrypoint: ["/bin/sh", "-c"]
command: ["exit 0"]
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER:?MINIO_ROOT_USER is required}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:?MINIO_ROOT_PASSWORD is required}
AI_PLATFORM_OBJECT_STORAGE_BUCKET: ${AI_PLATFORM_OBJECT_STORAGE_BUCKET:-ai-platform-uploads}
MC_CONFIG_DIR: /tmp/.mc
volumes:
- ${BACKUP_HOST_DIR:?BACKUP_HOST_DIR is required}:/backup
read_only: true
tmpfs:
- /tmp
security_opt:
- no-new-privileges:true
depends_on:
minio:
condition: service_healthy
volumes:
postgres-data:
name: ${POSTGRES_VOLUME_NAME:-yuqei-v2-production-postgres}
minio-data:
name: ${MINIO_VOLUME_NAME:-yuqei-v2-production-minio}
contract-files:
name: ${CONTRACT_FILES_VOLUME_NAME:-yuqei-v2-production-contract-files}
secrets:
ai_provider_api_key:
file: ${AI_PROVIDER_API_KEY_FILE:?AI_PROVIDER_API_KEY_FILE must point to a protected provider key file}
rc06_facade_bearer_token:
file: ${RC06_FACADE_BEARER_TOKEN_FILE:?RC06_FACADE_BEARER_TOKEN_FILE must point to a protected token file}
rc06_kms_credential:
file: ${RC06_KMS_CREDENTIAL_FILE:?RC06_KMS_CREDENTIAL_FILE must point to a protected credential file}