llms.txt and robots.txt were served without a charset, so browsers decoded the valid UTF-8 as Windows-1252 and showed mojibake (â€", für). - Add `charset utf-8;` (+ charset_types) so text responses carry `; charset=utf-8`. - Add an explicit `location ~* \.txt$` that serves the file as plain text, returns 404 instead of falling back to the SPA index.html, and sets `Access-Control-Allow-Origin: *` so any AI crawler/tool can fetch llms.txt / llms-full.txt cross-origin. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Visigine — Docker
Single-stack compose: nginx serves the built React app on port 8080 and
reverse-proxies /api/* to the backend container over the internal docker
network. The browser sees one origin — no CORS dance.
Prerequisites
- Docker Desktop / Docker Engine
docker composev2 plugin (bundled with current Docker Desktop)
First run
# 1. Make sure server/.env exists with MISTRAL_KEY and ADMIN_TOKEN.
# If you only used the dev flow so far, server/.env is already populated.
cat server/.env
# 2. Build + start (from repo root)
docker compose -f docker/docker-compose.yml up -d --build
# 3. Open
# UI: http://localhost:8080
# Admin: http://localhost:8080/admin
# Health: http://localhost:8080/health
Daily ops
# Logs (follow)
docker compose -f docker/docker-compose.yml logs -f
# Restart after backend code change
docker compose -f docker/docker-compose.yml up -d --build backend
# Restart after frontend code change
docker compose -f docker/docker-compose.yml up -d --build frontend
# Stop everything
docker compose -f docker/docker-compose.yml down
# Stop + remove built images
docker compose -f docker/docker-compose.yml down --rmi local
What runs where
| Container | Image | Port | Role |
|---|---|---|---|
visigine-frontend |
nginx:alpine | 8080 |
Serves dist/ + proxies /api/* |
visigine-backend |
node:20 | 3001* |
Express API, Mistral, checks |
*Backend port is internal-only. It is expose:d, not ports:d — nothing
on your host can hit :3001 directly. The frontend reaches it via the
docker network DNS name backend:3001.
Configuration
- Secrets (
MISTRAL_KEY,ADMIN_TOKEN): loaded from../server/.env. - Deployment vars: hard-set in
docker-compose.yml. To run on a different host port or domain:- Change
ports: "8080:80"to whatever you want on the host side. - Update
ALLOWED_ORIGINSto match the public URL the browser uses.
- Change
Notes
NODE_ENV=production→?debug=1is unconditionally stripped from responses. The admin dashboard still gets full debug data through/api/admin/analyze(gated byADMIN_TOKEN).ALLOW_PRIVATE_HOSTS=0→ SSRF guard is fully on. Don't flip this in prod.- Nginx strips the browser
Originheader before forwarding, so the backend's CORS allow-list only governs cross-origin browsers hitting:3001directly — which can only happen if you manually-p 3001:3001.