Document that pravda.education is no longer DevPlace and what this host runs

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 08:37:37 +02:00
co-authored by Claude Sonnet 5.5
parent b616bce0ea
commit fad41d0fbe
2 changed files with 14 additions and 17 deletions
+13 -16
View File
@@ -385,11 +385,11 @@ On every commit the hook increments the patch component (`1.0.0` -> `1.0.1`) and
This procedure exists because a single "the editor is down" report turned out to be **three unrelated faults stacked on each other** (a stale URL, a firewalled network leg, and a corrupt database), and the investigation wasted hours by guessing before measuring. Work the layers outward from the browser; each step is cheap and each one eliminates a whole class of cause. **Never skip to a hypothesis, and never repair anything before the layer above it is proven healthy.**
**Layer 0 - is the request even arriving here?** Fetch the hostname over the public internet exactly as it resolves (`curl -sS -o /dev/null -w '%{http_code} %{remote_ip}' https://host/`). Compare the answering IP against this machine's own addresses (`ip -6 addr`, `curl https://api.ipify.org`). Two hostnames serve this platform by different routes - see the topology section above. **Never use `curl --resolve` to force a hostname onto an IP it does not resolve to**; that fabricates a path that no real traffic takes and produces confident, wrong conclusions.
**Layer 0 - is the request even arriving here?** Fetch the hostname over the public internet exactly as it resolves (`curl -sS -o /dev/null -w '%{http_code} %{remote_ip}' https://host/`). Compare the answering IP against this machine's own addresses (`ip -6 addr`, `curl https://api.ipify.org`). DevPlace is served publicly on `devplace.net` only - see the topology section below. **Never use `curl --resolve` to force a hostname onto an IP it does not resolve to**; that fabricates a path that no real traffic takes and produces confident, wrong conclusions.
**Layer 1 - which edge answered?** The error body identifies it. `application/problem+json` with `No site configured for host` is molohttp. A DevPlace HTML error page is the application. An nginx error page is the nginx container. A browser `ERR_*` with no body means nothing well-formed was returned at all.
**Layer 1 - which edge answered?** The error body identifies it. A DevPlace HTML error page is the application. An nginx error page is the nginx container. A browser `ERR_*` with no body means nothing well-formed was returned at all.
**Layer 2 - same failure on both hostnames?** Run the identical authenticated request against `pravda.education` and `devplace.net`. Failing on **both** means the application or the database; failing on **one** means that host's edge. This single comparison is the highest-value measurement available and costs one command.
**Layer 2 - same failure on the public hostname and on the host itself?** Run the identical authenticated request against `https://devplace.net` and `http://127.0.0.1:10500`. Failing on **both** means the application or the database; failing only on `devplace.net` means the front host's proxy or the SSH tunnel. This single comparison is the highest-value measurement available and costs one command.
**Layer 3 - the application log, before any theory.** `docker logs --since 5m devplace-app-1`. Count error classes rather than reading prose (`grep -c malformed`). A recurring service-loop error is a systemic fault even when it looks unrelated to the symptom.
@@ -410,23 +410,20 @@ This procedure exists because a single "the editor is down" report turned out to
## Production hostnames and the devplace.net SSH tunnel (verified topology, do not re-derive)
**The platform answers on two public hostnames, and they reach the same application by two completely different paths.** This has already cost one debugging session; the failure mode is that a `curl --resolve devplace.net:443:<production ip>` "test" reports `No site configured for host: devplace.net` and looks like a total outage, when in fact devplace.net never touches the production edge at all.
**DevPlace answers publicly on `devplace.net` only.** `pravda.education` used to be a second hostname for it; it is now a different site. Measured 2026-10-14: it still resolves to this machine (`95.216.15.238`) but answers with `Server: grr` and a different application (`/robots.txt` and `/openapi.json` are 404, `/docs` is 200 where DevPlace returns 302), and the owner confirmed it is no longer DevPlace. Never use it to verify a DevPlace change, and never read a 404 there as a DevPlace fault.
| | `pravda.education` | `devplace.net` |
|---|---|---|
| DNS | `95.216.15.238`, `2a01:4f9:2a:100e::2` | `88.198.21.243`, `2a01:4f8:222:2c45::2` |
| Machine | the production host itself | a separate front host (Hetzner, PTR `static.88-198-21-243.clients.your-server.de`) |
| Path in | molohttp on `:443` -> `127.0.0.1:10500` | its own proxy -> **SSH tunnel** -> `127.0.0.1:10500` on production |
| Reaches molohttp | yes | **no, never** |
| | `devplace.net` |
|---|---|
| DNS | `88.198.21.243`, `2a01:4f8:222:2c45::2` |
| Machine | a separate front host (Hetzner, PTR `static.88-198-21-243.clients.your-server.de`) |
| Path in | its own proxy -> **SSH tunnel** -> `127.0.0.1:10500` on this host |
**`devplace.net` is a front host that forwards over SSH.** It holds a persistent SSH session into the production host (visible there as an established inbound connection from `88.198.21.243` to port 22) and forwards through it to `127.0.0.1:10500`, which is the `docker-proxy` for the `devplace-nginx` container. The listening socket lives on the **front** host (an `ssh -L` style local forward), so the production host shows **no** sshd-owned listener - that absence is expected and is not evidence against the tunnel.
**`devplace.net` is a front host that forwards over SSH.** It holds a persistent SSH session into this host (visible here as an established inbound connection from `88.198.21.243` to port 22) and forwards through it to `127.0.0.1:10500`. In the Docker Compose layout that port is the `docker-proxy` for the `devplace-nginx` container; on a host running `make dev` it is uvicorn itself. The listening socket lives on the **front** host (an `ssh -L` style local forward), so this host shows **no** sshd-owned listener - that absence is expected and is not evidence against the tunnel.
Two consequences that must not be forgotten:
- **The front host's proxy is the edge to suspect when only `devplace.net` fails** while `127.0.0.1:10500` is healthy (WebSocket `Upgrade` headers are the usual culprit, exactly as for the production nginx locations below).
- **The machine the repository is checked out on may run `make dev`, not the Compose stack** (observed 2026-10-14: `uvicorn --reload` on port 10500, no `docker` binary, `podman` present). `make docker-reload` fails there with `docker: No such file or directory`, and the `--reload` server already serves committed code. Check `ss -ltnp | grep 10500` and `which docker` before assuming a deploy path.
- **molohttp has no `devplace.net` site, and that is correct.** Its site list is `mail`/`smtp`/`imap.molodetz.nl`, `pravda.education` and `*.tunnel.pravda.education`. devplace.net traffic enters below molohttp, straight into `127.0.0.1:10500`, so it needs no site. **Never "fix" this by adding a devplace.net site to molohttp** - devplace.net does not resolve to the production host, so such a site could never match, and its absence is not a bug.
- **Both hostnames land on the same nginx and the same app**, so a request that fails on both is failing in the application, not in either edge. That comparison is the fastest triage available here: run the same authenticated request against both hostnames. Same failure on both means look at the app or the database; a failure only on devplace.net means look at the front host's proxy (WebSocket `Upgrade` headers are the usual culprit, exactly as for the production nginx locations below).
**Testing rule.** Never point a hostname at an IP it does not resolve to in order to "test" it. Fetch each hostname over the public internet as it really resolves (`curl https://devplace.net/...` and `curl https://pravda.education/...`), because forcing devplace.net onto the production IP tests molohttp with a `Host` it deliberately does not serve and proves nothing about the real path.
**Testing rule.** Never point a hostname at an IP it does not resolve to in order to "test" it. Fetch `https://devplace.net/...` over the public internet as it really resolves, and compare it with `http://127.0.0.1:10500/...` on the host itself.
## Production deployment
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "devplacepy"
version = "1.0.19"
version = "1.0.20"
description = "DevPlace - The Developer Social Network"
requires-python = ">=3.12"
dependencies = [