## Summary
- `chat_web_app` nginx now has exact locations for `/robots.txt`, `/sitemap.xml`, and `/llms.txt`.
- Each location serves the static file or returns 404. It does not fall through to `index.html`.
- Other static apps are unchanged. The flag is `exact_root_files` on `chat_web_app` only.
- Merged current `master`, so the Stripe webhook proxy stays in place next to the new locations.
- Closes#43.
The file contents ship in the chat_web_app public-site PR.
## Test plan
- [ ] Render the web-static nginx template and confirm only `chat_web_app` gets the three exact locations
- [ ] Webhook location `/api/finance/webhooks/` is still present
- [ ] After deploy, `curl -I https://hesychia.ai/robots.txt` is `text/plain`, and a missing file would be 404 rather than HTML
Reviewed-on: #44
## Summary
- Closes#41.
- Stripe live endpoint `we_1TzDNHDV0RPXOyxGCOftavHV` posts to `https://hesychia.ai/api/finance/webhooks/stripe/`. On 2026-10-09 the `chat_web_app` prod vhost returned HTTP 405 fifteen times (`request_time` 0.000, User-Agent Stripe). `pending_webhooks` stayed 1.
- `try_files` falls through to `index.html`, and nginx rejects POST on that static file. The Django handler on `chat_backend` never sees the body.
- `chat_web_app` now proxies `/api/finance/webhooks/` to the same-env `chat_backend` port (`8003` prod, `8013` beta) via `host.docker.internal`. `Host` is the backend public hostname so `ALLOWED_HOSTS` matches. Other static apps are unchanged.
## Test plan
- [ ] `ansible-playbook playbooks/site.yml --syntax-check` (or the web-static portion) renders without error
- [ ] Redeploy web-static (`scripts/deploy.sh` path that runs the web-static role) so `nginx.conf` reloads
- [ ] `curl -i -X POST http://127.0.0.1:8082/api/finance/webhooks/stripe/` from an app host reaches Django (400 invalid signature or 503 missing secret), not 405
- [ ] Stripe Dashboard: resend a failed event to the Hesychia.ai endpoint and confirm 2xx
Reviewed-on: #42