Work / Nexus / Changelog / Day 7
Day 7: CORS and the Path to MinIO
Preflight now returns 204. Console points at gateway :3100. Presigned MinIO URLs use a browser-reachable host. Five tests cover CORS and the artifact download path.
Day 7 is CORS plus proving Console → Gateway → MinIO without the browser getting blocked.
CORS middleware was already in create_app(). It was not enough. Preflight returned 200 instead of 204. Allowed headers were only Authorization and Content-Type. The Console defaulted to gateway port 8080. The real gateway is 3100. And a presigned MinIO URL generated inside compose would have been http://minio:9000/..., which the browser cannot open.
What changed
Gateway CORS. Subclass of Starlette’s middleware so OPTIONS preflight is HTTP 204 with Access-Control-Allow-Origin, credentials, methods, and headers. Auth still skips OPTIONS. 401 JSON from the JWT middleware still gets CORS headers, so a logged-out Console can read the error instead of a opaque CORS failure.
Console URL. defaults.ts now falls back to http://localhost:3100. The Console Dockerfile bakes VITE_API_GATEWAY_URL at build time with the same default. Vite .env already had 3100; Docker did not.
MinIO from the browser. Gateway setting NEXUS_GW_MINIO_PUBLIC_ENDPOINT=localhost:9000 rewrites the internal minio:9000 host on presigned URLs. Compose sets MINIO_API_CORS_ALLOW_ORIGIN so the follow-up GET from the SPA origin is allowed.
Tests
Five passed:
- Preflight 204 + CORS headers for
http://localhost:5173 - Unknown origin is not reflected
- Unauthenticated GET still includes CORS headers on 401
- Authenticated list artifacts → presigned URL, CORS on both
- Public endpoint rewrite strips
minio:9000from the URL
This is not a live compose loop on my machine tonight. It is the contract the loop needs. Bring up deploy/compose/dev.yml, open the Artifacts panel, pick Skills, download. If MinIO is empty you get an empty list with CORS intact, not a browser error.
Skill from the work: docs/skills/code-console-gateway-minio-browser-path.md.
What I am not claiming
Wazuh still is not in this path. Skills listed from MinIO are not yet the git docs/skills/ copies unless something uploaded them. Day 7 is the pipe, not the inventory.
Later note (Aug 29): lab still defaults to MinIO. Cloudflare R2 is a prod/k8s overlay on the same S3 client (NEXUS_GW_OBJECT_STORE_BACKEND=r2) — it does not replace this Day 7 browser path; it changes where the bucket lives.
Next is Day 8: the Settings page is still a stub.