Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 30 additions & 5 deletions docs/ai/deployment_integration_plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
51 changes: 51 additions & 0 deletions infra/deploy/docker-compose.dgx.yml
Original file line number Diff line number Diff line change
@@ -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
Loading