The repo had two near-duplicate no-cache dev servers. The root dev-server.py
(serves web/ via directory=, documented in the README) is canonical; web/dev-server.py
was a later re-implementation that served CWD and required 'cd web' first. Keeping
both meant every change had to be written twice and drifted — PR #16 had to paste
--host into both files.
Delete the web/ copy and drop its now-stale .dockerignore entry. No docs or scripts
reference it; the README and Docker path both point at the root server.
The web app is a buildless static SPA (three.js is vendored in web/), so the
deploy image is just nginx serving web/ on :80. This adds:
- Dockerfile: nginx:stable-alpine, copies web/ + LICENSE, OCI labels.
- nginx.conf: port 80 site config that mirrors web/_redirects
(/index.html and /index 301 -> /) via a $request_uri guard that cannot
loop with the `index` directive's internal redirect; cache policy mirrors
dev-server.py (no-store on the app's own JS/CSS/HTML) while long-caching
the vendored three.js tree (immutable per image tag); absolute_redirect
off so the redirect stays relative behind a reverse proxy.
- docker-compose.yml: one `web` service, host 8731:80 so the URL matches
`python3 dev-server.py`, with a wget healthcheck.
- .dockerignore: excludes prototype/, plugin/, tests/, docs/, the dev
servers and model files so the build context is just the web artifact.
- README: "Run with Docker" block pointing at the new files.
Validated: image builds, `nginx -t` passes, GET / -> 200 no-store,
GET /index.html|/index -> 301 relative /, GET /vendor/... -> immutable
long-cache, no redirect loop on /.