From 830c050f723cf81ab4dec6f11d0ba0bfad219c8f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 13:36:12 +1000 Subject: [PATCH] Keep the Phase 9 compose additions inert until Phase 9 The design commit added ai-docs-worker with no profile and an inbox mount on ai-api. Both take effect the moment anyone deploys, and neither has the code or the directories behind it yet: - ai-compose.yml's own header says to run a bare `up -d`. That would start ai-docs-worker with entrypoint `python worker.py` - a file that does not exist - under restart: unless-stopped, so it crash-loops on a live shared host that also runs the demo PLC. - Docker creates a missing bind source as a ROOT-OWNED directory. ai-api does not run as root, so deploying the inbox mount before the directory exists gives the API an inbox it cannot write to, on the growing disk. So: profiles: [worker] on the worker, matching the ai-ingest precedent, and the ai-api volume block commented out with the install command beside it - the same "add each piece at the phase that needs it" convention as caddy/ai-routes.caddy. A bare `up -d` from main now starts exactly what it started before: pg-ai, cube, ai-api, ai-web. Both guards come off in the commit that adds worker.py. Co-Authored-By: Claude Opus 5 --- BUILD-AI-CONTAINERS.md | 2 +- compose/ai-compose.yml | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/BUILD-AI-CONTAINERS.md b/BUILD-AI-CONTAINERS.md index d03c45a..97974bf 100644 --- a/BUILD-AI-CONTAINERS.md +++ b/BUILD-AI-CONTAINERS.md @@ -582,7 +582,7 @@ Deployed early, deliberately: from here on, every experiment is traced. **Tasks** 1. `db/004_doc_uploads.sql` — the `doc_uploads` queue, the `live_documents` view, and the `uploads_rw` / `ingest_rw` roles. `agent_ro` gains nothing. Then `db/005_doc_actions.sql` — the `doc_actions` trail, the `withdrawn_documents` view, the column-level `superseded` grant and the withdraw-only trigger. -2. `/datadisk/ai-docs-inbox` — the writable staging area. **Check `df -h /datadisk` first.** Never on `/`. +2. `/datadisk/ai-docs-inbox` and `/datadisk/ai-docs-withdrawn` — the writable staging and archive areas. **Check `df -h /datadisk` first.** Never on `/`. Create them owned by uid 10002 *before* uncommenting the `ai-api` volume block in `compose/ai-compose.yml`, and drop `profiles: [worker]` from `ai-docs-worker` in the same commit that adds `worker.py` — both are guarded so that a deploy from `main` today starts nothing that does not exist yet. 3. `ai-api`: the `/docs/*` router — upload, list, detail, approve, reject, withdraw, restore, purge. Identity comes from Authelia's forwarded headers, never from the request body. Approval requires the publisher group and is refused without it, whatever Authelia allowed through. 4. `ai-docs-worker` — the `ingest` image with `worker.py` as its entrypoint. Long-running, `ai-internal` only, no published port. Pre-scans uploads for a header proposal, ingests approved ones, and completes withdrawals, restores and purges. `/datadisk/ai-docs-withdrawn` is created and mounted with the other two. - **While you are in `ingest.py`: make `mark_superseded()` move the superseded file out of the tree as well.** Without that, `--all` re-ingests it as live — see the defect note in section 16.10. diff --git a/compose/ai-compose.yml b/compose/ai-compose.yml index 8367b1e..03c3691 100644 --- a/compose/ai-compose.yml +++ b/compose/ai-compose.yml @@ -93,11 +93,19 @@ services: networks: [ai-internal, proxy] env_file: - /home/azureuser/ai/api.env # 0600, not in Git - volumes: - # Phase 9 - the upload inbox, and the ONLY writable path this container - # has. Deliberately not /datadisk/ai-docs: a file that has been uploaded - # but not yet approved must not be visible to `ai-ingest --all`. - - /datadisk/ai-docs-inbox:/inbox + # Phase 9 - the upload inbox, and the ONLY writable path this container + # gets. Deliberately not /datadisk/ai-docs: a file that has been uploaded + # but not yet approved must not be visible to `ai-ingest --all`. + # + # COMMENTED OUT UNTIL PHASE 9, on the same principle as caddy/ai-routes.caddy: + # add each piece at the phase that needs it. Docker creates a missing bind + # source as a ROOT-OWNED directory, and ai-api does not run as root - so + # deploying this before the directory exists gives you an inbox the API + # cannot write to, on the growing disk of a live shared host. + # Create it first, then uncomment: + # sudo install -d -o 10002 -g 10002 /datadisk/ai-docs-inbox + # volumes: + # - /datadisk/ai-docs-inbox:/inbox healthcheck: test: ["CMD", "python", "-m", "app_healthcheck"] interval: 30s @@ -171,6 +179,11 @@ services: dockerfile: Dockerfile image: yau/ai-ingest:local container_name: ai-docs-worker + # Kept out of `up -d` by the profile, exactly as ai-ingest is. worker.py + # does not exist yet, so an unguarded service here would crash-loop on a + # live shared host. Drop the profile in the same commit that adds the file. + # docker compose -f ~/ai-compose.yml --profile worker up -d ai-docs-worker + profiles: [worker] restart: unless-stopped depends_on: pg-ai: