# CI overlay for the end-to-end smoke test. Never part of a real deployment. # # `minimal` deliberately starts no embedder, so booting the whole loop in CI # needs one supplied. This adds a stub that answers the /embeddings contract # with a constant vector — enough to drive the permission loop, where the ACL # pre-filter and not relevance decides what a search returns. # # It also pulls MinIO forward into `minimal`, which no real deployment should # do — object storage is `full`, and for a licensing reason (AGPLv3; see # docs/licensing.md). CI needs it because **binary ingest requires a bucket**: # a PDF's bytes have no other home, so the smoke cannot prove that path # without one. The text half of the loop runs through the bucket too as a # result, which is coverage the smoke did not have before. # # Used only as `docker compose -f docker-compose.yml -f docker-compose.ci.yml`, # so it is invisible to `docker compose --profile … config` and to the # lint:compose check, which read the base file alone. services: # `minimal` as well as `full`. Both are named because Compose's merge rule # for a sequence — append to the base, or replace it — decides what this # becomes, and `minimal` has to survive either answer. minio: profiles: [minimal, full] # The bucket-creating one-shot now lives in the base file, in `full`, because # a real deployment needs it for the same reason CI does. Only its profile # moves here — same as minio above, and for the same merge reason. minio-init: profiles: [minimal, full] stub-embedder: image: node:24-alpine restart: unless-stopped command: ["node", "/opt/stub/stub-embedder.mjs"] environment: PORT: "8000" # Kept in step with the .env the smoke workflow writes, so the vector # width matches the collection init builds. NACRE_DEFAULT_EMBEDDING_DIM: "${NACRE_DEFAULT_EMBEDDING_DIM:-8}" volumes: - ./scripts/ci:/opt/stub:ro healthcheck: test: [ "CMD", "node", "-e", "fetch('http://localhost:8000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))", ] interval: 3s timeout: 2s retries: 10 start_period: 3s