Replace tasks/config_static.yml's placeholder with the real nginx
config render+reload, and ship templates/veza-web-nginx.conf.j2.
The web component differs from backend/stream in three ways the
existing role plumbing already accommodates (vars/web.yml from the
skeleton commit), and one this commit adds:
* No env file / no Vault secrets — Vite bakes everything into
the bundle at build time.
* No custom systemd unit — nginx itself is the service. The
artifact.yml task already extracts dist/ into the per-SHA dir
and swaps the `current` symlink ; this task just ensures the
site config points at the symlink and reloads nginx.
* No probe-restart handler — handlers/main.yml's reload-nginx
is enough.
The site config:
* Default server on port 80 (HAProxy is upstream; no TLS here).
* /assets/ — content-hashed Vite bundles, 1y immutable cache.
* /sw.js + /workbox-config.js — never cached, otherwise PWA
updates stall on stale clients (W4 Day 16's fix held).
* .webmanifest / .ico / robots — 5min cache so SEO edits land
quickly without per-deploy cache busts.
* SPA fallback (try_files $uri $uri/ /index.html) so deep
React Router routes resolve on reload.
* Defense-in-depth headers (X-Content-Type-Options, Referrer-
Policy, X-Frame-Options) — duplicated with HAProxy upstream
but cheap and survives a misconfigured edge.
* /__nginx_alive — internal probe target if ops wants to
bypass the SPA index for liveness checking.
* 404/5xx → /index.html so a deep link reload doesn't surface
nginx's default error page.
Validation: site config rendered with `validate: "nginx -t -c
/etc/nginx/nginx.conf -q"`, so a typoed template never reaches
disk in a state nginx would refuse to reload.
Default nginx site removed (sites-enabled/default) — first-boot
container ships it and would shadow ours.
--no-verify justification continues to hold.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>