Unknown URLs were returning the app shell with status 200. Return 404 for missing paths, keep the extensionless pages, and send the security headers. Production short links redirect for HeyCatch.
Co-authored-by: Cursor <cursoragent@cursor.com>
## 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
Each host mounts wp-content and gets a compose file from inventory instead of a hand-copied one. Also allow LAN TCP 8020 so abc_voice is reachable through UFW.
## Summary
- Register `abc_voice` as a second container from `GIS/abc_worker` (`docker-compose.voice.yml`), beta port **8020** on adama, roslin, starbuck, apollo, and ai-server-4080.
- Document `https://beta.abc.voice.aimloperations.com` and `~/Documents/secrets/abc_voice/abc_voice_beta.env`. Ansible does not create the NPM host.
- Closes#38.
## Test plan
- [ ] Create the secret file (copy the worker beta env, set `VOICE_ENABLED=true`, Cartesia, and LiveKit) before the first deploy
- [ ] `deploy.sh --app abc_voice --env beta` after the worker compose file is on `Development`
- [ ] NPM: `beta.abc.voice.aimloperations.com` → `:8020` on all five hosts
- [ ] `abc_be` `WORKER_AV_URL=https://beta.abc.voice.aimloperations.com`
Merge this before the abc_worker workflow PR, or that deploy step fails looking up `abc_voice`.
Reviewed-on: #39
## Summary
- A no-ref beta deploy of `abc_be`, `abc_worker`, and `abc_fe` checks out `Development` instead of `master`.
- `livekit` and every other app still use their current default branch.
- CI that passes `--ref` is unchanged; this only affects the catalog fallback.
## Test plan
- [ ] `scripts/deploy.sh --app abc_be --env beta` resolves ref `Development`
- [ ] Same for `abc_worker` and `abc_fe`
- [ ] `livekit` still resolves `master`
Reviewed-on: #37
## Summary
- Closes [#27](#27).
- Register `print_forge` in `app_catalog` (`type: django`, repo `ai_ml_operations/print_forge`, default branch `master`).
- Add `host_apps` prod `:8007` / beta `:8019` on **adama**, **roslin**, **starbuck**, **apollo**, and **ai-server-4080**.
- dj-queue **worker singleton on adama** only (`compose_profiles: [worker]`); other hosts web-only.
- Document ports, NPM hosts, Postgres DBs, secrets, Nominatim (`10.0.0.128:8089`), and the app-repo companion ([print_forge#1](ai_ml_operations/print_forge#1)).
- Update `scripts/deploy.sh` `--app` help.
- Move unused `abc_be` prod port reserve **8007 → 8009** so it does not collide with `print_forge`.
## Test plan
- [ ] Confirm `print_forge` appears in `app_catalog` and `--app print_forge` is listed in `deploy.sh --help`
- [ ] Confirm beta `host_apps` on all five hosts (port **8019**); adama has `compose_profiles: [worker]`
- [ ] Confirm prod port **8007** reserved on those hosts (no NPM until launch)
- [ ] After merge (ops, not this PR): create Postgres DBs `print_forge_beta` + `print_forge` on `10.0.0.230`, grant `westfarn`
- [ ] After merge (ops): write `~/Documents/secrets/print_forge/print_forge_{beta,prod}.env` from app `.env.prod.example`; add `print_forge:<token>` to `SHORTENER_API_TOKENS` (beta first)
- [ ] After merge (ops): NPM `print-forge-preview.aimloperations.com` → `:8019` active/active; do **not** NPM-route `printforgeprints.com` until launch
- [ ] After secrets + DBs: `~/Documents/repos/server-infra/scripts/deploy.sh --app print_forge --env beta --ref master` and hit `/healthz/`
Reviewed-on: #28
## Summary
- Closes [#20](#20).
- Register **starbuck** (`10.0.0.44`) and **apollo** (`10.0.0.7`) as Proxmox app hosts in `webservers`, with `host_apps` copied from **roslin** (same apps/ports; **no** `monica_site` worker — that stays a singleton on **adama**).
- Allow `starbuck` / `apollo` in `provision.sh` and `deploy.sh`; document inventory, NPM backends, and Alloy `host` labels.
## Test plan
- [ ] `ansible-inventory --list` shows both hosts under `webservers` at the expected IPs.
- [ ] Bootstrap SSH + passwordless sudo on each VM (keep a Proxmox console open for first UFW enable).
- [ ] `ansible starbuck,apollo -m ping`
- [ ] `./scripts/provision.sh starbuck --check` then `./scripts/provision.sh starbuck`
- [ ] `./scripts/provision.sh apollo --check` then `./scripts/provision.sh apollo`
- [ ] `./scripts/deploy.sh starbuck` and `./scripts/deploy.sh apollo` bring up the same app+env set as roslin.
- [ ] `monica_site` dj-queue worker still runs **only** on adama.
- [ ] Grafana/Loki show `host="starbuck"` and `host="apollo"`.
- [ ] NPM Advanced upstreams (manual): add `starbuck:PORT` and `apollo:PORT` beside adama/roslin.Reviewed-on: #21
## Summary
- Change `dta_webapp` `webroot_pattern` from `/var/www/{env}.app.ditchtheagent/html` → `/var/www/{env}.realpath.app/html`
- Update `IMPLEMENTATION.md` references to match
- **web-static:** before `compose up`, `docker rm -f` any container publishing the static host ports (`docker ps --filter publish=<port>`), then force-recreate when cleanup ran — clears orphan/legacy binders while keeping the same ports
Closes#12
Coordinates with [dta_webapp#48](Ditch_The_Agent/dta_webapp#48) / [PR #49](Ditch_The_Agent/dta_webapp#49) (`build:beta`/`build:prod` write to the same dirs; prod uses `prod.realpath.app`).
## Test plan
- [ ] Confirm `app_catalog.dta_webapp.webroot_pattern` expands to beta/prod realpath paths
- [ ] On a host with a leftover container on 8080/8081, run deploy and confirm it is removed and web-static rebinds
- [ ] After merge, redeploy `dta_webapp` beta (and prod when ready) and verify files under `/var/www/{env}.realpath.app/html`
- [ ] Hit web-static ports and confirm new build content servedReviewed-on: #13
## Summary
- Closes [#10](#10).
- Deploys **SearxNG** on **ai-server-4080** (`10.0.0.128`) for [chat_backend#62](ai_ml_operations/chat_backend#62) / [PR #65](ai_ml_operations/chat_backend#65) grounded search.
- New `roles/searxng/` (compose + JSON-enabled `settings.yml`), gated by `searxng_stack: true`, wired into `site.yml`.
- **Host port 8088** (not 8080 — that is `dta_webapp` on this host). UFW allows `10.0.0.0/24` → `8088/tcp` only.
## Ops after merge
```bash
./scripts/provision.sh ai-server-4080
# or targeted:
ansible-playbook playbooks/site.yml --limit ai-server-4080 --tags never # full site play includes searxng when searxng_stack
```
Then set in `chat_backend_prod.env` / `chat_backend_beta.env`:
```text
SEARCH_PROVIDER=searxng
SEARCH_FAILOVER_PROVIDER=ddgs
SEARXNG_BASE_URL=http://10.0.0.128:8088
```
Smoke test from any app host:
```bash
curl -sG 'http://10.0.0.128:8088/search' --data-urlencode 'q=test' -d 'format=json' | head
```
## Test plan
- [ ] Provision ai-server-4080; confirm `docker ps` shows `searxng`
- [ ] Confirm `:8088` responds with JSON; `:8080` still serves dta_webapp
- [ ] Confirm UFW rule is LAN-only
- [ ] From adama/roslin container network, curl SearxNG succeeds
- [ ] Update chat_backend secrets to `:8088` and redeploy betaReviewed-on: #11
Add app_catalog entry, host_apps on adama/roslin/ai-server-4080 (prod :8082),
and document ports/NPM backends for the CRA static frontend.Reviewed-on: #4
## Summary
- Register `scha` in `app_catalog` and `host_apps` on adama, roslin, and ai-server-4080 (prod port **8002**; beta **8012** reserved).
- Update `IMPLEMENTATION.md` ports, apps, Postgres, and NPM tables so every app lists beta + prod.
- Include `scha` in `deploy.sh` help.
Part A (dockerize / CI in the `scha` repo) is tracked separately in [scha#19](ai_ml_operations/scha#19).
## Test plan
- [ ] Confirm inventory YAML loads: `ansible-inventory --list` shows `scha` under each host
- [ ] After Part A merges + `~/Documents/secrets/scha/scha_prod.env` exists: dry-run `./scripts/deploy.sh --app scha --env prod --check`
- [ ] Create DB `scha` on shared Postgres (`10.0.0.230`) and point NPM at `adama:8002` + `roslin:8002` (+ optional `ai-server-4080:8002`)
Reviewed-on: #3
ai-server-4080 runs dta_service active/active with adama and roslin.
Disable beta_dta_wsgi/prod_dta_wsgi so port 8001/8011 are free for compose.
Co-authored-by: Cursor <cursoragent@cursor.com>