From 53882101f86c8229db36b75d76ccf637a7c0a7af Mon Sep 17 00:00:00 2001 From: Dae Hyeon Kim Date: Mon, 20 Jul 2026 16:26:22 +0900 Subject: [PATCH 1/2] feat(deploy): DGX standalone compose (ai-server only, 24855->8001) + runbook with confirmed topology MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - infra/deploy/docker-compose.dgx.yml: standalone (never an override) — no postgres/api services, distinct project/container names, env_file .env with PROMPTS_BASE_DIR re-pinned to baked /app/prompts, extra_hosts host-gateway fallback for DB hairpin, restart unless-stopped - runbook §3: confirmed DGX topology (DB 28881 / ai-server 24855 on one host), 4-command deploy, backend base URL http://223.194.33.26:24855 - refs docs/ai/deployment_integration_plan.md G1-G3 --- docs/ai/deployment_integration_plan.md | 35 +++++++++++++++--- infra/deploy/docker-compose.dgx.yml | 51 ++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 infra/deploy/docker-compose.dgx.yml diff --git a/docs/ai/deployment_integration_plan.md b/docs/ai/deployment_integration_plan.md index bff93ce..e776929 100644 --- a/docs/ai/deployment_integration_plan.md +++ b/docs/ai/deployment_integration_plan.md @@ -29,12 +29,37 @@ Deferred (P2 backlog): idempotency guard, request_id propagation (REV-001 majors), narrative activation (gated), FHIR $validate external option. -## 3. Deployment runbook (Docker) +## 3. Deployment runbook (Docker) — DGX topology confirmed 2026-07-20 -1. Local: `docker compose -f infra/deploy/docker-compose.yml build ai-server` → container smoke (boot, /health, route smoke). -2. Transfer: `docker save` → `ssh DGX docker load` (or private registry). -3. AI server: place `.env` (operator), `docker compose up -d`; in-compose DB via `postgres:5432`, external DB via 28881 if split later — env-DSN only. -4. Verify: `/health` 200, DB preflight, prompt SHA pins, one F1-turn + one F5-report smoke. +Confirmed topology (user, 2026-07-20): one DGX Spark host at `223.194.33.26` runs the DB +server and the AI server as separate containers with distinct external ports. + +| Service | External | Internal | State | +|---|---|---|---| +| DB server (postgres, admin-managed container) | `223.194.33.26:28881` | 5432 | running | +| ai-server (this deployment) | `223.194.33.26:24855` | 8001 | to deploy | + +The dev workstation (`192.168.68.62`) has no docker and needs none — deployment executes on +the DGX. Dedicated standalone compose file: `infra/deploy/docker-compose.dgx.yml` +(ai-server only; defines no postgres/api, distinct project+container name, production CMD, +no source mounts, `PROMPTS_BASE_DIR` pinned to the baked `/app/prompts` so the dev `.env`'s +workstation-relative value cannot leak in via env_file). Never combine it with the dev +`docker-compose.yml`. + +On the DGX, as the account that runs the DB container: + +```bash +git clone https://github.com/Neuro-AI-Lab/neurosync.git && cd neurosync # or git pull +# place apps/ai-server/.env (operator copies from the dev workstation; never committed) +docker compose -f infra/deploy/docker-compose.dgx.yml up -d --build +curl -s localhost:24855/health +``` + +Verify: `/health` 200 → route smoke (`POST /ai/survey/plan`, `/ai/temporal/analyze`, +`/ai/handoff/report`) → DB preflight. If the container cannot reach the DB through the +host's external IP (hairpin), change the DSN host in `.env` to +`host.docker.internal:28881` (extra_hosts maps it) — env edit only. +Backend base URL: `http://223.194.33.26:24855`. ## 4. Gates qa (CI-mirror + route contract tests + container build/boot smoke) → clinical-validator quick pass on /ai/survey/plan semantics (safety-net/SI-supplement decisions must match the validated behavior) → critic wording/regression check. Local commits only; publish on user's word. diff --git a/infra/deploy/docker-compose.dgx.yml b/infra/deploy/docker-compose.dgx.yml new file mode 100644 index 0000000..f856aac --- /dev/null +++ b/infra/deploy/docker-compose.dgx.yml @@ -0,0 +1,51 @@ +# DGX Spark production deployment — ai-server ONLY. +# +# ⚠️ STANDALONE file, NOT an override of docker-compose.yml — run with ONLY +# `-f infra/deploy/docker-compose.dgx.yml`. Never combine the two files. +# +# - The DB server already runs on this DGX as a separately managed container +# (external 28881 → postgres 5432). This file deliberately defines NO +# postgres and NO api service — never start a second DB from here. +# - Distinct compose project name + container name → zero state collision +# with any compose project or container already on the DGX. +# - External port 24855 is the allocated ai-server port. Backend base URL: +# http://223.194.33.26:24855 (container listens on 8001, Dockerfile CMD). +# - All secrets come from apps/ai-server/.env via env_file at RUN time; the +# image never contains .env (root .dockerignore excludes it). The operator +# places .env on the DGX before `up`. +# +# Run from repo root on the DGX: +# docker compose -f infra/deploy/docker-compose.dgx.yml up -d --build +# curl -s localhost:24855/health +# +# DB reachability: DATABASE_URL in .env points at 223.194.33.26:28881. If the +# container cannot hairpin through the host's external IP, change only the DSN +# host to host.docker.internal:28881 (mapped via extra_hosts below) — an .env +# edit, no code change. + +name: neuro-sync-ai-dgx + +services: + ai-server: + build: + context: ../.. + dockerfile: apps/ai-server/Dockerfile + container_name: ns-ai-server-dgx + # No `command:` override and no src/tests volume mounts — the baked image + # is the deployment artifact; the Dockerfile's production CMD runs + # (no --reload). + env_file: + - ../../apps/ai-server/.env + environment: + LOG_LEVEL: info + # `environment:` outranks env_file in compose precedence. The dev .env + # carries the workstation-relative PROMPTS_BASE_DIR=docs/ai/prompts, + # which does not exist inside the container — pin the baked-in copy + # (Dockerfile: COPY docs/ai/prompts /app/prompts) so env_file injection + # can never break prompt loading. + PROMPTS_BASE_DIR: /app/prompts + ports: + - "24855:8001" + extra_hosts: + - "host.docker.internal:host-gateway" + restart: unless-stopped From 1d8449ede445a49ef726298413d397d3dc7a64e5 Mon Sep 17 00:00:00 2001 From: Dae Hyeon Kim Date: Mon, 20 Jul 2026 16:47:09 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix(deploy):=20.dockerignore=20=E2=80=94=20?= =?UTF-8?q?re-include=20docs/ai/prompts=20for=20the=20image=20COPY;=20excl?= =?UTF-8?q?ude=20=5Farchive=20from=20build=20context?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 'docs' exclusion predates the Dockerfile's COPY docs/ai/prompts (e4bb0bb) and would fail the DGX image build at that COPY step; negations re-include only the prompts subtree - _archive (42MB, 1,856 files) no longer inflates the build context - refs docs/ai/deployment_integration_plan.md §3 --- .dockerignore | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.dockerignore b/.dockerignore index b85c091..b944c80 100644 --- a/.dockerignore +++ b/.dockerignore @@ -57,10 +57,19 @@ build # Large source materials (kept locally, never shipped) references -# Docs — not needed at runtime +# Docs — not needed at runtime, EXCEPT the versioned agent prompts, which the +# ai-server image bakes in (apps/ai-server/Dockerfile: COPY docs/ai/prompts +# /app/prompts + ENV PROMPTS_BASE_DIR). Without these negations the COPY fails +# at build time ("not found" — the whole docs tree would be absent from the +# build context). docs +!docs/ai/prompts +!docs/ai/prompts/** README.md +# Archived legacy materials — segregated for traceability, never shipped +_archive + # Local data *.sqlite *.db