Live drive: §2/§2b/§3 deploy+health+routing findings (F5 uptime-kuma 404 cascade, F6-F8)

This commit is contained in:
2026-06-14 00:14:11 +02:00
parent ce7f44421f
commit ddc1ed7fab
+23
View File
@@ -55,5 +55,28 @@ The demo dashboard has **no password set**, so the controller's `RequireAuth` an
### F3 — Hungarian catalog text double-UTF-8-encoded in API JSON — LOW (verify)
- `GET /api/stacks` returns descriptions like `SzemĂ©lyes pĂ©nzĂĽgyek` (mojibake for "Személyes pénzügyek"). Looks like double-encoding. Need to confirm whether the rendered HTML UI is affected or only the JSON API. Severity TBD.
### F5 — uptime-kuma: broken catalog healthcheck → permanently unhealthy → **total 404 outage via Traefik** — HIGH
- **Area:** §2 deploy / §2b health detection / §3 routing — a cascade.
- **Action:** deployed uptime-kuma (`POST /api/stacks/uptime-kuma/deploy`), observed state, then traced the 404.
- **Expected:** deploy → healthy → status.demo-felhom.eu serves the app (200).
- **Actual / evidence — the cascade:**
1. **Catalog bug:** `/opt/docker/stacks/uptime-kuma/docker-compose.yml` defines `healthcheck.test: ["CMD","node","/app/extra/healthcheck.mjs"]`, but in the `louislam/uptime-kuma:2` image that file does not exist → `docker inspect` health log: `Error: Cannot find module '/app/extra/healthcheck.mjs'`, `Health=unhealthy FailingStreak=5`.
2. The **app process is actually fine**`curl http://uptime-kuma:3001/` from a peer container → **302** (serving).
3. **Traefik gates route registration on Docker health.** On the websecure (443) entrypoint, `status.demo-felhom.eu → 404`, while every healthy app (tasks/recipes/share/paste) → 200. The HTTP→HTTPS redirect on :80 returns 301 for *all* hosts (global catch-all), which masks the missing 443 router. So the unhealthy container's TLS router is never published.
4. **Net result:** uptime-kuma is **completely unreachable at its URL (404)** for the customer, despite the app running — purely because of a wrong healthcheck path in the catalog.
- **Verdict:** broken. **Severity: HIGH.** Two issues to file: (a) catalog healthcheck wrong for uptime-kuma:2; (b) **design risk***any* app with a broken/too-slow healthcheck doesn't just show "unhealthy", it becomes a hard 404 outage. The controller's deploy returns success and the dashboard shows "deployed (unhealthy)", giving no hint that the URL is dead. Consider surfacing "route not published because unhealthy" to the operator.
- **Good part:** the controller **did correctly detect and surface `unhealthy`** (`GET /api/stacks/uptime-kuma``state=unhealthy`) — health detection itself works.
### F6 — Deploy POST returns `"deployed"` optimistically, before compose completes / before health is known — MEDIUM (API contract)
- **Action:** timed `POST /api/stacks/<app>/deploy` vs controller logs.
- **Evidence:** vikunja POST returned ~0s but log shows compose took 3.4s; uptime-kuma POST returned ~0s but compose pull took **31.7s** and the app ended **unhealthy**. The response `{"ok":true,"message":"Stack <app> deployed"}` is sent before the container is up and regardless of eventual health.
- **Impact:** This is the documented in-memory `Deployed=true`-before-`compose up` pattern (avoids the card flipping back mid-pull), and the UI compensates by polling `GET /api/stacks/<app>`. But the **API message "deployed" is misleading** — an API consumer (or a script) that trusts the POST result will think a broken/unhealthy app succeeded (see F5). Verdict: clunky/misleading message; not a data-integrity bug. **Severity: MEDIUM.**
### F7 — API `state` lags Docker health by ~10s after deploy — LOW
- mealie's container reported `(healthy)` in `docker ps` ~12s before `GET /api/stacks/mealie` flipped from `starting` to `running`. Cosmetic polling lag; transient. **Severity: LOW.**
### F8 — cloudflared `TUNNEL_TOKEN` stored in plaintext in docker-compose.yml — LOW/INFO
- The cloudflared infra stack's compose holds `TUNNEL_TOKEN=<redacted>` in plaintext on disk (not `enc:`-wrapped like app.yaml secrets). May be acceptable for an infra/base-bringup stack, but worth confirming against the "secrets must be encrypted at rest" posture. Token redacted here. **Severity: LOW/INFO.**
### F4 — `/api/stacks/rescan` returns "stack not found: rescan" — LOW
- The runbook's documented rescan endpoint `GET /api/stacks/rescan` is routed as a stack name lookup → `{"ok":false,"error":"stack not found: rescan"}`. Either the route was removed/renamed or the runbook is stale. (Sync/rescan is reachable via `POST /api/sync`.) Cosmetic but documents a stale/missing endpoint.