Files
ai-contract-platform/yuqei-infra/README.md
T

18 KiB

Yuqei V2 Infrastructure

This directory contains the local and deployment baseline for the V2 rebuild.

Local Compose Startup

From repository root:

Copy-Item yuqei-infra\compose\.env.example yuqei-infra\compose\.env
docker compose --env-file yuqei-infra\compose\.env -f yuqei-infra\compose\docker-compose.yml up --build

Services:

  • Contract Web: http://localhost:3000
  • Contract API: http://localhost:8100
  • AI Platform API: http://localhost:8101
  • MinIO S3 API: http://localhost:9000
  • MinIO Console: http://localhost:9001

Migration Order

The compose file initializes databases and runs explicit Alembic migrations before starting the APIs:

  • postgres-init-databases
  • ai-platform-api-migrate
  • contract-api-migrate

Application containers keep *_DATABASE_AUTO_CREATE=false so schema changes are owned by migrations instead of implicit table creation.

The local compose baseline uses one PostgreSQL instance, but separates V2 data into dedicated databases:

  • yuqei_contract: Contract Platform business data.
  • yuqei_ai: AI Platform runtime, RAG, Prompt, Trace, and quality data.
  • yuqei_ops: infrastructure and operations data reserved for later services.

postgres-init-databases runs before migrations and creates missing databases idempotently. The two active platforms also keep separate Alembic histories:

  • ai_platform_alembic_version
  • contract_api_alembic_version

Contract API migrations must only target yuqei_contract; AI Platform and worker migrations/runtime data must only target yuqei_ai. Cross-platform references should use IDs such as ai_trace_id, never direct cross-database joins.

Runtime Verification

After services are up:

powershell.exe -NoProfile -ExecutionPolicy Bypass -File yuqei-infra\scripts\verify-v2-runtime.ps1

The script checks:

  • Contract API /health.
  • Contract API /api/v1/runtime/config-check.
  • AI Platform /health.
  • AI Platform worker runtime health.
  • AI Platform object storage read/write/delete probe.
  • Contract Web /health page availability.

Remote Smoke Deploy

Use this to validate the V2 stack in an isolated directory and on independent ports at 192.168.3.90 before any separately authorized production promotion.

Default remote path:

/opt/apps/ai-contract-platform-v2-smoke

Default smoke URLs:

  • Contract Web: http://192.168.3.90:13000
  • Contract API: http://192.168.3.90:18100
  • AI Platform API: http://192.168.3.90:18101
  • MinIO Console: http://192.168.3.90:19001

Run from repository root:

$env:SSHPASS='YOUR_192_168_3_90_ROOT_PASSWORD'
powershell.exe -NoProfile -ExecutionPolicy Bypass -File yuqei-infra\scripts\deploy-v2-smoke-docker.ps1 `
  -UseSshpass `
  -AllowDirtyWorktree

The script:

  • Packages only V2 source and infrastructure directories.
  • Excludes local .env* runtime files from the release archive.
  • Uploads the archive to 192.168.3.90.
  • Starts compose with yuqei_contract, yuqei_ai, and yuqei_ops.
  • Runs health checks for Contract Web, Contract API, and AI Platform API.
  • Runs the AI golden sample asset gate inside the ai-platform-api container.
  • Checks that the three V2 databases exist.
  • Runs the contract review worker --run-once smoke command.
  • Writes deploy artifacts to project-docs\qa-artifacts\v2-smoke-docker-deploy.

Latest passing smoke artifact:

  • project-docs\qa-artifacts\v2-smoke-docker-deploy\2026-07-16T11-12-47
  • AI golden sample asset gate passed with 90 samples, 6/6 suites, and 16/16 checks.
  • Contract review worker smoke completed.
  • Remote database baseline passed for yuqei_contract, yuqei_ai, and yuqei_ops.
  • Contract API runs with one Uvicorn worker and a 2-second read cache; all mutating requests invalidate the cache before and after execution.

After smoke passes, production promotion remains a deliberate, separately authorized action: backend/AI/worker/database/MinIO on 192.168.3.90, frontend on Aliyun, and contact.yuqei.com kept as the public entry.

Production is now a separate, guarded path. Use compose/docker-compose.production.yml together with the shared compose file, then follow deploy/README.md. The production path requires digest-pinned images, loopback-only internal ports, TLS edge configuration, an off-host backup mount, protected provider secret files, and a passing deploy/preflight-production.py. It intentionally has no automatic traffic-cutover command.

For shared-staging real-Provider acceptance, keep the Provider key in an owner-only host file outside the repository and add compose/docker-compose.staging-provider.yml to the Compose command. Set AI_PROVIDER_API_KEY_FILE to that absolute host path and configure the enabled default database Provider with api_key_secret_ref=file:///run/secrets/ai_provider_api_key. The staging override clears the direct Provider-key environment variable and mounts the file read-only; never place the key in .env.smoke, API payloads, or artifacts.

Real-provider acceptance uses scripts/run-ai-provider-acceptance.py. It exercises 10 legal QA and 8 contract-review Phase13 samples and separates technical Provider success from named legal approval. The collect step produces a deterministic, redacted technical review package. The protected shared-staging registration/finalize step requires a named legal Ed25519 approval envelope, a trusted legal public-key ring, and server-side content-hash verification of the shared evidence object. The old HMAC attestation is rejected by the protected API. The existing asset-count golden gate does not replace this runtime evaluation.

For the asynchronous model-task release baseline, first run the AI API in worker dispatch mode and start one or more workers with unique knowledge and model worker names. Then execute the resumable 30-case baseline:

python yuqei-infra\scripts\run-model-job-quality-baseline.py `
  --base-url http://127.0.0.1:8101 `
  --run-id fixed-v1-20260826 `
  --execute

Stable idempotency keys make the run safe to resume. The report counts only provider_status=succeeded outputs that passed through the current quality gate; disabled-provider and fallback responses never satisfy the 30-sample requirement. JSON and Markdown evidence is written under .runtime/model-job-quality-baseline and is intentionally excluded from source control.

For the controlled-pilot capacity and stratified quality gate, plan the fixed 100-case suite first, then execute it only against an isolated tenant with up to ten current-code model workers:

python yuqei-infra\scripts\run-controlled-pilot-stratified-quality.py
python yuqei-infra\scripts\run-controlled-pilot-stratified-quality.py `
  --execute `
  --base-url http://127.0.0.1:8101 `
  --run-id stratified-v1-20260826

The runner covers ten contract types with ten cases each, runs 1/3/5/10 concurrency waves, retries only Provider failures or fallback, and leaves high-risk or sampled outputs in the auditable human-review queue. Its ignored artifact contains metrics and identifiers only, not question or answer text.

After a named legal reviewer signs the exact collect output, the finalize step validates that package, registers it in shared staging, fetches the server-normalized receipt payload, and signs receipt v2 with a CI-held Ed25519 key:

python yuqei-infra\scripts\run-ai-provider-acceptance.py `
  --mode finalize `
  --base-url https://staging-api.example.com/ai `
  --service-key-file C:\protected\staging-service-key `
  --review-package-file C:\protected\provider-technical-review-package.json `
  --legal-approval-file C:\protected\named-legal-approval.json `
  --legal-keyring-file C:\protected\legal-trusted-keyring.json `
  --receipt-private-key-file C:\protected\provider-acceptance-ed25519.pem `
  --receipt-key-id provider-acceptance-2026q3 `
  --target-release-id v2.0.0-rc.1 `
  --target-image-digest sha256:<64-hex-image-digest> `
  --receipt-output C:\release-evidence\provider-acceptance-receipt.json `
  --promotion-bundle-output C:\release-evidence\provider-promotion-bundle.json

The receipt signing private key and legal approval private key stay in staging/CI. Production receives only the signed receipt, a public-key ring, the receipt file SHA-256 pin, and the matching immutable RELEASE_ID and image digest. compose/docker-compose.production.yml mounts both JSON files read-only, and deploy/preflight-production.py rejects missing or mismatched evidence, signing material, legal approval material, and staging database credentials. Production readiness verifies the receipt offline and never reads the shared-staging acceptance or audit tables.

Contract generation and clause recommendation are intentionally outside the Provider acceptance path because those product flows are deterministic Contract API operations. Run their independent golden gate from the repository root with a Python environment that has the Contract API dependencies installed:

python yuqei-infra\scripts\run-contract-product-golden.py

This gate invokes the real Contract API template renderer for 23 generation cases and the real review-rule/clause recommendation logic for 8 cases. It preserves unresolved required business fields as explicit template placeholders. A technical pass reports needs_legal_review until the inventory contains verifiable named legal and business approvals; it never creates or persists an acceptance_run_id.

Release Quality Gate

Use this after a smoke deploy and interface benchmark to turn the release checks into one pass/fail gate:

When service authentication is enabled, load the protected smoke values into process environment variables first. Do not pass the values on the command line or write them to the artifact directory.

$env:CONTRACT_API_WEB_PROXY_SHARED_SECRET = "<load from protected smoke environment>"
$env:AI_PLATFORM_SERVICE_SHARED_SECRET = "<load from protected smoke environment>"
python yuqei-infra\scripts\run-release-quality-gate.py `
  --web-base-url http://192.168.3.90:13000 `
  --contract-api-base http://192.168.3.90:18100 `
  --ai-api-base http://192.168.3.90:18101 `
  --allow-ai-provider-warning

The gate verifies:

  • Contract API /api/v1/runtime/schema-check.
  • AI Platform API /api/v1/runtime/schema-check.
  • Latest Docker smoke summary under project-docs\qa-artifacts\v2-smoke-docker-deploy.
  • Latest interface benchmark summary under project-docs\qa-artifacts\v2-interface-benchmark.

To refresh the benchmark during the same gate run:

python yuqei-infra\scripts\run-release-quality-gate.py `
  --web-base-url http://192.168.3.90:13000 `
  --contract-api-base http://192.168.3.90:18100 `
  --ai-api-base http://192.168.3.90:18101 `
  --run-benchmark `
  --allow-ai-provider-warning

When --run-benchmark is enabled, the release gate defaults to 15 measured requests per scenario, concurrency 3, and 2 warmup requests. Including the benchmark probes, this remains below the strictest enforced read limit of 20 requests per user per minute. The standalone interface benchmark retains its larger load-test defaults; only raise the release-gate values in an isolated performance environment where the intended anti-abuse response is evaluated separately.

To require fresh summaries without rerunning every check:

python yuqei-infra\scripts\run-release-quality-gate.py `
  --web-base-url http://192.168.3.90:13000 `
  --contract-api-base http://192.168.3.90:18100 `
  --ai-api-base http://192.168.3.90:18101 `
  --max-smoke-summary-age-hours 24 `
  --max-benchmark-summary-age-hours 24

Artifacts are written to project-docs\qa-artifacts\v2-release-quality-gate. GitHub Actions also exposes a manual Release Quality Gate workflow for a self-hosted runner with access to 192.168.3.90.

--allow-ai-provider-warning is only for a smoke environment that intentionally disables real Provider calls and enables fallback. Omit it for production readiness; any provider_runtime warning then fails the gate. Both the gate and benchmark read service keys from environment variables and record only whether authentication was configured, never the values.

Latest passing release gate artifact:

  • project-docs\qa-artifacts\v2-release-quality-gate\2026-07-13T14-03-15
  • Run mode: --run-benchmark --allow-ai-provider-warning, with protected service keys loaded from environment variables.
  • Contract API schema-check, the expected smoke-only AI Provider warning, Docker smoke summary, and 7 interface benchmark scenarios passed.
  • Each scenario ran 60 measured requests at concurrency 6 with 0% errors. Contract list p95 was 199.31 ms and knowledge chunk hybrid search p95 was 66.74 ms.

Latest supporting artifacts:

  • Interface benchmark: project-docs\qa-artifacts\v2-release-quality-gate\2026-07-13T14-03-15\interface-benchmark.
  • Security scan: project-docs\qa-artifacts\v2-security-scan\2026-07-13-auth-hardening-final-2.
  • Source secret scan: PR CI for commit 6abca4eb passed npm audit, Bandit, pip-audit, and Linux detect-secrets with no unreviewed source secret.

Contract Review Worker Flow Verification

Use this after the APIs and worker are running to verify the T7 contract review path:

powershell.exe -NoProfile -ExecutionPolicy Bypass -File yuqei-infra\scripts\verify-contract-review-worker-flow.ps1 `
  -ContractApiBaseUrl http://localhost:8100 `
  -WorkerExecutionMode docker-compose `
  -RequireConfidence

The script creates a temporary contract, uploads a readable source text file, starts a review task, runs or waits for the contract review worker, then checks that the review result was written back with an AI Trace id. Add -RequirePrimarySources in staging/production when the AI Platform knowledge base is expected to return legal or policy sources.

-WorkerExecutionMode supports:

  • api: call POST /api/v1/review-worker/run-once.
  • docker-compose: run python -m yuqei_contract_api.review_worker --run-once inside the contract-review-worker service.
  • none: do not trigger manually; wait for a background worker loop to consume the task.

Core Product Launch Readiness

Use the core launch gate while dual-cloud KMS probes and immutable witnesses are deferred. It validates the live APIs and public pages, runs a fixed regression suite for the first contract journeys, and separates engineering failures from missing real-model, legal-policy, and Provider-acceptance materials.

python yuqei-infra\scripts\run-core-launch-readiness.py --profile development
python yuqei-infra\scripts\run-core-launch-readiness.py --profile controlled-pilot

The development profile permits external-material warnings so product work can continue. The controlled-pilot profile fails closed until the public AI disclosure, Provider quality/legal acceptance, and all four effective policies are verified. Dual-cloud external trust is reported separately as an optional enterprise capability and never becomes a core gate while its runtime mode is disabled or optional.

Artifacts are written to project-docs\qa-artifacts\core-launch-readiness; every run contains a JSON summary, Markdown summary, JUnit XML, the complete core-journey test log, and a 31-case deterministic contract generation/clause-recommendation golden summary. The test subprocess pins PYTHONPATH to the current workspace source roots so a stale editable installation cannot produce a false-positive result.

Production Baseline

Cloud KMS gateway overlay

Enterprise Playbook calendar credentials can use the isolated Alibaba Cloud or Tencent Cloud KMS gateway. Build and publish yuqei-infra/docker/kms-gateway.Dockerfile as an immutable image, then add exactly one overlay to the production compose invocation:

yuqei-infra/compose/docker-compose.production.kms-aliyun.yml
yuqei-infra/compose/docker-compose.production.kms-tencent.yml

The gateway must remain internal-only. Cloud credentials, the gateway service token, TLS material, CA files, and the temporary legacy recovery key are read-only file mounts; do not put their values in Compose environment entries. After /health reports configured=true, complete a real round-trip and persistent recovery-canary drill before queuing a rewrap job. Deployment and rollback gates are documented in project-docs/playbook-cloud-kms-rewrap-fault-drill-runbook-2026-08-25.md.

Production must override the local demo defaults:

CONTRACT_API_ENVIRONMENT=production
CONTRACT_API_DATABASE_URL=postgresql+psycopg://.../yuqei_contract
CONTRACT_API_DATABASE_AUTO_CREATE=false
CONTRACT_API_SEED_DEMO_DATA=false
CONTRACT_API_AI_PLATFORM_BASE_URL=http://ai-platform-api:8101
CONTRACT_API_AI_PLATFORM_TIMEOUT_SECONDS=60
CONTRACT_API_UVICORN_WORKERS=1
CONTRACT_API_READ_CACHE_TTL_SECONDS=2
CONTRACT_API_REVIEW_WORKER_NAME=contract-review-worker-1
CONTRACT_API_REVIEW_WORKER_POLL_INTERVAL_SECONDS=30
CONTRACT_API_REVIEW_WORKER_BATCH_LIMIT=5
AI_PLATFORM_ENVIRONMENT=production
AI_PLATFORM_DATABASE_URL=postgresql+psycopg://.../yuqei_ai
AI_PLATFORM_DATABASE_AUTO_CREATE=false
AI_PLATFORM_UVICORN_WORKERS=1
YUQEI_WORKER_DATABASE_URL=postgresql+psycopg://.../yuqei_ai
YUQEI_WORKER_DATABASE_AUTO_CREATE=false

Keep CONTRACT_API_AI_PLATFORM_TIMEOUT_SECONDS at least five seconds above AI_PLATFORM_PROVIDER_REQUEST_TIMEOUT_SECONDS; production preflight rejects missing, non-finite, out-of-range, or inverted timeout budgets.

Start from yuqei-infra\compose\.env.production.example and replace every CHANGE_ME value before deployment.

The production inventory requires immutable digest references for Contract API, Contract Web, AI Platform API, and AI Worker. The production override clears the shared development build definitions with the Compose Specification !reset tag. On a host whose images were imported offline, append compose/docker-compose.production.offline.yml last so every production service, including backend-hosted-web and ops profile services, uses pull_policy: never. See deploy/README.md for the complete file order.

Run migrations explicitly before replacing application containers.

The 2-second read cache is process-local and is automatically disabled when CONTRACT_API_UVICORN_WORKERS is greater than 1. Use a shared cache with cross-process invalidation before increasing the production worker count if the same list/detail p95 target must be retained.