docs(monitor): record phase 1 as deployed + prod verification results

Phase 1 is live on cochi. The deploy exercised the rotation path on its own —
monitor came up against the pre-restart latest.log, Minecraft created a new
one, tailer reported rotations:1 and kept streaming — which was the single
failure this design was most likely to hit.

Also records the one known cosmetic gap: a delete-then-recreate rotation takes
the "file was gone" branch, so the rotations counter does not tick. Streaming
is unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
oierandClaude Opus 5 committed 2026-08-01 22:56:26 +02:00
1 parent fca6747bcc
commit 53604a6127
1 file changed
+37 -8
+37 -8
View File
@@ -247,16 +247,17 @@ tail-across-rotation. None of it is in the tiles. Once a password-gated page
shows raw log lines, everything after is a small additive commit with a rolling shows raw log lines, everything after is a small additive commit with a rolling
one-container restart. one-container restart.
### Phase 1 — thin slice: logs only ### Phase 1 — thin slice: logs only — **DONE, live on cochi 2026-08-01**
- [ ] DNS A record for `monitor.${BASE_DOMAIN}`; add to `LE_SUBDOMAINS`; issue cert. - [x] DNS A record for `monitor.${BASE_DOMAIN}`; add to `LE_SUBDOMAINS`; issue cert.
- [ ] `docker/monitor/` — Go service: ring buffer, tailer (inode/shrink reopen), (DNS already resolved — the zone has a wildcard, so no record was needed.)
- [x] `docker/monitor/` — Go service: ring buffer, tailer (inode/shrink reopen),
cold-start backfill, `GET /api/log?since=`, `GET /healthz`, embedded UI. cold-start backfill, `GET /api/log?since=`, `GET /healthz`, embedded UI.
- [ ] `ui/index.html` — log pane, sticky autoscroll, filter, level chips. - [x] `ui/index.html` — log pane, sticky autoscroll, filter, level chips.
- [ ] compose service (no `ports:`, `mc_data:/data:ro`). - [x] compose service (no `ports:`, `mc_data:/data:ro`).
- [ ] `caddy/conf.d/80-monitor.caddy` + `MONITOR_USER`/`MONITOR_PASSWORD_HASH`. - [x] `caddy/conf.d/80-monitor.caddy` + `MONITOR_USER`/`MONITOR_PASSWORD_HASH`.
- [ ] nginx vhost + `MONITOR_ENABLED` gate; render + reload. - [x] nginx vhost + `MONITOR_ENABLED` gate; render + reload.
- [ ] Deploy with `--hard` (world downtime). Verify below. - [x] Deploy with `--hard` (world downtime, 0 players online). Verified below.
### Phase 2 — tiles ### Phase 2 — tiles
@@ -301,3 +302,31 @@ Then the one that actually matters, because it is the failure this design is
most likely to hit: **restart the Minecraft server and confirm the stream keeps most likely to hit: **restart the Minecraft server and confirm the stream keeps
moving.** That is the rotation path, it fires on every deploy, and a broken moving.** That is the rotation path, it fires on every deploy, and a broken
tailer stays HTTP-200 while going silent. tailer stays HTTP-200 while going silent.
### Phase 1 results (cochi, 2026-08-01)
The deploy itself exercised the rotation path: monitor came up while the old
`latest.log` still existed, Minecraft then created a new one, and the tailer
reported `rotations: 1` and kept streaming. Verified in prod:
| Check | Result |
|---|---|
| `GET /` no creds / wrong creds / right creds | 401 / 401 / 200 |
| TLS | `CN=monitor.ulicraft.net`, Let's Encrypt |
| Buffer after boot | 908 lines, `tail.ok: true` |
| Stream advancing | +109 lines in 12s, `dropped: 0`, `reset: false` |
| Rotation across the MC restart | `rotations: 1`, stream uninterrupted |
| Port 8080 from the internet | connection refused |
| `docker compose port monitor 8080` | `:0` (nothing published) |
| Host listeners on :8080 | none |
Also exercised locally before deploy, against a synthetic log: backfill capped
to buffer size, partial (unterminated) lines withheld until their newline lands,
`mv`-style rotation, in-place truncation, delete-and-recreate, ring overflow
(`dropped: 15, reset: true`), a cursor from the future, malformed `since` (400),
unknown path (404).
**Known cosmetic gap:** a rotation done as delete-then-recreate (rather than
`mv`) does not increment the `rotations` counter — the tailer takes the
"file was gone, reopen" branch instead of the rotation branch. Streaming is
unaffected; only the displayed count is.