V2 production backend deployment
The production backend runs on 192.168.3.90; Contract Web is deployed separately to Alibaba Cloud. The production compose override therefore keeps Contract Web behind the opt-in backend-hosted-web profile and exposes only the HTTPS edge service publicly by default. Its image is still required and digest-pinned so every production profile has one immutable image inventory.
Current closeout execution boundary
The current RC-05 through RC-08 release must follow the repository mandatory delivery policy and the production acceleration plan:
- Do not build a formal release while relevant source is still changing. Deployment consumes one reviewed, immutable commit, source manifest, and image inventory.
- Every build, SBOM, scan, install package, UAT record, and recovery record must bind to the same commit and image digests.
- Promote the exact image digests that passed UAT; do not rebuild nominally equivalent images before production.
- Read-only preflight may prepare evidence, but deployment, migration, rollback, traffic cutover, real credential/data use, and risk acceptance still require the appropriate named authorization.
- A proven blocker remains fail-closed. Efficiency targets never authorize bypassing a production gate.
Files
../compose/docker-compose.yml: shared service definitions used by local and smoke environments.../compose/docker-compose.production.yml: production image, volume, TLS edge, and backup-helper overrides. It uses the Compose Specification!reset nulltag to remove every applicationbuildinherited from the shared file.../compose/docker-compose.production.offline.yml: final, offline-only override that forbids registry pulls for every service that production can enable, including optional profiles.../compose/docker-compose.production.smtp.yml: optional SMTP password secret mount; include only when email delivery mode issmtp.../compose/.env.production.example: reviewed variable inventory. The real file belongs at/etc/yuqei/production.env, mode0600, outside the repository.preflight-production.py: read-only production configuration gate.verify-production-compose.py: read-only merged-model gate. It renders Docker Compose JSON for online and offline modes across the default,backend-hosted-web,ops, and combined profile sets.release-package.py: backend-only release-package verifier, planner, and guarded executor. Package v1 is read-only compatibility input; package v2 can reach apply only after all eleven image imports, third-party materials, the selected recovery strategy, external evidence, and exact authorization pass validation.plan-first-release-snapshot-restore.py: verifies first-release pre-install snapshot/backup/restore-test evidence and can mint a short-lived install-readiness receipt. It emits plans and receipts only; it does not restore data.plan-rollback-rehearsal.py: verifies the frozen package and source-bound release tools before emitting a non-writing rollback rehearsal plan.../scripts/build-infrastructure-image-bundle.py: file-only validator for the four application and seven infrastructure image archives, security reports, content-addressed layers, and offline import plan.../scripts/build-third-party-materials.py: deterministic third-party notice, license-text, and corresponding-source material builder/verifier.../scripts/assemble-build05-v2-package.py: read-only-by-default BUILD-05 package preflight and atomic assembler. It only composes already validated application, infrastructure, third-party, recovery, and external-evidence inputs;--assembleis required to create an output outside the source repository.../release-trust/third-party-materials-baseline.json: source-frozen technical trust baseline. The shipped baseline intentionally remainsNOT_PROVISIONEDuntil reviewed real catalog, license, and source identities are supplied.
Prepare without changing traffic
- Create an immutable RC commit and publish the four application images by digest: Contract API, Contract Web, AI Platform API, and AI Worker. Pin PostgreSQL/pgvector, Redis, MinIO, mc, Nginx, and the backup helper by digest as well.
- Create a new production PostgreSQL/volume set. Do not reuse or rename the smoke database or volumes.
- Mount an off-host backup filesystem at
BACKUP_HOST_DIR. - Place
tls.crt,tls.key, and the AI provider key in protected host directories. When enabling SMTP, also create the file named byCONTRACT_API_IDENTITY_SMTP_PASSWORD_FILE_HOST, mode0600. - Copy
.env.production.exampleto/etc/yuqei/production.env, replace every placeholder, and keep mode0600. Create the file named byPRODUCTION_ALERT_WEBHOOK_URL_FILE, containing only the approved HTTPS alert receiver URL. Keep it owner-only and readable by thenobodyaccount used by the production health unit (for example, ownernobody:nobody, mode0600). - Run the read-only gate:
python3 yuqei-infra/deploy/preflight-production.py \
--env-file /etc/yuqei/production.env \
--artifact-dir /var/lib/yuqei/preflight
- Validate the merged Compose models, then pull online images without starting services:
python3 yuqei-infra/deploy/verify-production-compose.py \
--env-file /etc/yuqei/production.env
docker compose \
--project-name yuqei-v2-production \
--env-file /etc/yuqei/production.env \
-f yuqei-infra/compose/docker-compose.yml \
-f yuqei-infra/compose/docker-compose.production.yml \
pull
The merge gate invokes only docker compose config --format json; it does not
start, build, pull, or remove anything. The separate pull command downloads
the already approved online image references, but does not start containers or
switch traffic.
Use a current Docker Compose V2 release that implements the Compose Specification
!reset tag. A successful config --quiet invocation proves only that Compose
can parse and interpolate the files; it does not prove the merged image, build,
or pull-policy content. The JSON merge gate performs those semantic checks.
For an offline host, import every approved image into the local engine through
the approved artifact-transfer procedure. The JSON merge gate always appends
docker-compose.production.offline.yml after the base and production files when
it verifies offline mode.
Pass --profile backend-hosted-web and/or --profile ops when those services are
intended. The offline override sets pull_policy: never for all services in every
core production profile, so a missing local image fails closed instead of contacting
a registry. A companion overlay that introduces another image-bearing service needs
an explicit offline pull policy of its own before it is approved for this path. Do
not add the offline override to the online pull command.
release-package.py keeps package v1 available for read-only historical
verification. Package v2 binds four application images and seven infrastructure
images, their offline archives and import order, exact Compose image IDs,
third-party materials, and one explicit recovery strategy. A v2 apply is still
fail-closed unless the fixed technical trust baseline is provisioned, the raw
external recovery evidence is independently revalidated, all dynamic blockers
are empty, and the exact operation authorization is present. The executor then
loads, tags, and inspects each image before Compose and runs its built-in
post-apply checks; a failure leaves reconciliation state instead of being treated
as success. The first-release recovery planner never performs a restore, and a
successful technical verification is not legal approval, distribution
clearance, UAT evidence, production authorization, or public-release approval.
Contract Web remains an identity-bound, separately deployed artifact, so this
backend executor also does not claim that the cross-host release was installed.
Third-party material inputs must bind one exact package-use boundary. The
internal-hosted profile means same-legal-entity internal use with no external
conveyance; it still requires exact occurrence coverage, license and notice
texts, and corresponding source for every SPDX expression containing an AGPL
term. The redistributable profile covers external conveyance or customer
distribution and requires corresponding source for every restricted occurrence.
Compound SPDX expressions are evaluated conservatively. The release package
binds the selected profile and distribution scope and rejects a mismatch. These
are technical completeness rules only; neither profile grants legal approval or
distribution clearance.
The production override also replaces ai-platform-prompt-assets-import with the
importer's default dry-run command. That Compose service may validate and report the
packaged asset delta, but it does not publish those assets. Run the importer with
--apply only as a separately approved, version-pinned publication batch; a conflict
with an already published or archived version fails instead of overwriting that row.
This override does not govern repository bootstrap. The current AI API repository
initialization calls seed_defaults() and publishes one built-in default Prompt when
prompt_templates is empty. Production launch approval must therefore either start
with the approved Prompt baseline already present or separately close that bootstrap
behavior; the dry-run importer alone is not evidence that API startup performs zero
Prompt writes.
Optional SMTP secret
The default production example keeps identity email delivery disabled; it does not require an SMTP password file or the SMTP compose overlay. To enable SMTP, set the SMTP mode and related transport fields in the protected production env file, set manual invitations to false, then append the opt-in override to every production Compose command:
docker compose \
--project-name yuqei-v2-production \
--env-file /etc/yuqei/production.env \
-f yuqei-infra/compose/docker-compose.yml \
-f yuqei-infra/compose/docker-compose.production.yml \
-f yuqei-infra/compose/docker-compose.production.smtp.yml \
config --quiet
The override mounts CONTRACT_API_IDENTITY_SMTP_PASSWORD_FILE_HOST as /run/secrets/contract_smtp_password and clears the direct password environment variable. Do not include this override in disabled-mode deployments. The preflight gate checks that an authenticated SMTP configuration uses an absolute, non-empty, owner-only host secret file and an encrypted transport mode.
Provider secret and model config
The provider key file is mounted at /run/secrets/ai_provider_api_key. The enabled default model config must use:
api_key_secret_ref=file:///run/secrets/ai_provider_api_key
Do not send the key in the model-config API payload. Run the Provider acceptance gate before any production start approval.
The production Provider canary is read-only. Run the protected
provider-promotion-canary.yml workflow against at least two distinct HTTPS nodes after
the signed receipt, public keyring, immutable release and image digest are pinned. The
gate sends Cache-Control: no-cache, no-store and fails closed unless every response:
- declares
environment=productionandverification_mode=offline_read_only; - is fresh within
max_observation_age_seconds(120 seconds by default); - reports consistent top-level, check-detail, and promotion-diagnostic state; and
- matches the expected release, receipt, signing key and image digest.
Revocation and kill-switch modes apply the same provenance and freshness checks after all nodes report the blocking control. The workflow performs GET requests only; it does not change a keyring, toggle the kill switch, import a receipt, or invoke any identity write/execute API. Make control changes through the independently approved operations procedure, then use this workflow only to observe their propagation.
Public policy promotion
The production Compose mounts the verified policy candidate directory, named legal approval, and trusted public keyring into Contract API read-only. Keep CONTRACT_API_PUBLIC_POLICY_EXECUTOR_ENABLED=false; there is no HTTP write route.
Before activation, run the read-only production preflight and follow:
project-docs/V2-public-policy-production-promotion-runbook-2026-08-17.md
The named CLI performs status, preflight, dry-run, idempotent promotion, and historical rollback. A write requires --apply, the exact operation confirmation string, an owner-only authorization file, the approved expected generation/set, a trace ID, and an idempotency key. Policy private keys and the one-time authorization file must never be committed or added to Compose permanently.
Alibaba Cloud frontend variables
The frontend deployment must use server-only values:
CONTRACT_API_BASE_URL=https://<backend-domain>/contract
AI_PLATFORM_API_BASE_URL=https://<backend-domain>/ai
CONTRACT_API_PROXY_SECRET=<same protected proxy secret as Contract API>
AI_PLATFORM_PROXY_SECRET=<same protected AI service secret as AI Platform>
CONTRACT_WEB_SESSION_SECRET=<independent protected session secret>
CONTRACT_WEB_SESSION_SECURE=true
Do not expose any of these secrets through NEXT_PUBLIC_* variables. The browser should call only same-origin Contract Web API routes.
Traffic boundary
Starting postgres, redis, minio, migrations, APIs, and workers is a backend deployment. Starting edge or changing Alibaba Cloud frontend variables/DNS is a separate traffic-change approval. This repository intentionally does not provide an automatic production cutover command.