docs: fold the monitor service into the stack-level docs

plan/23-monitor.md already covered the design; this makes the monitor
discoverable from the places someone actually starts reading.

- README: stack table, vhost list, DNS names, layout, and a setup section
  covering the bcrypt credential, the no-published-port model, and the
  --hard-on-first-deploy requirement.
- plan/00-overview.md: drop (PLANNED), add to the always-on service list, and
  note that it starts fine with Minecraft down (missing log and refused RCON
  are reported states, not fatal).
- plan/19-routes.md: vhost row, the four GET routes with their contracts, and
  the internal mcnet edges (mc-status for rosters so it needs no Drasl creds,
  RCON for TPS, Kuma probing healthz around caddy).
- caddy/Caddyfile: the snippet index had drifted — it listed five of nine
  vhosts. Added the four missing ones.

Two gotchas promoted to CLAUDE.md because they cost real time and will recur:

- Compose interpolates $-sequences in .env VALUES, so a bare bcrypt hash is
  silently corrupted before reaching the container, and the same bug bites
  again when writing one to a remote .env over ssh. Also records that caddy
  and filestash want opposite hash formats.
- The monitor container runs as root deliberately: /data is drwxr-x--- owned
  by uid 1000, so a USER directive would make the tailer silently read nothing
  while still returning 200.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
oierandClaude Opus 5 committed 2026-08-01 23:30:42 +02:00
1 parent bc99427ef9
commit bac2372c95
5 files changed
+119 -4

No files matched your search

+6 -3
View File
@@ -152,10 +152,13 @@ mc-status, uptime-kuma and filestash.
- `18-landing-rework.md` — join flow / rosters / account / mod list / footer (PLANNED)
- `19-routes.md` — HTTP route reference (nmsr / landing / auth / files + internal)
- `20-files.md` — Filestash player file share (`files.`, one shared login)
- `23-monitor.md` — admin log stream + dashboard (`monitor.`, basic auth) (PLANNED)
- `23-monitor.md` — admin log stream + dashboard (`monitor.`, basic auth)
## Boot/dependency order
`caddy` (aliases) → `drasl` → `minecraft` (depends on drasl) → `mc-backup`.
`nmsr`, `mc-status`, `uptime-kuma` and `filestash` are always-on and join `mcnet`
(no ordering constraints — none of them are on the auth/boot path).
`nmsr`, `mc-status`, `uptime-kuma`, `filestash` and `monitor` are always-on and
join `mcnet` (no ordering constraints — none of them are on the auth/boot path).
`monitor` reads Minecraft's log volume and queries its RCON, but starts fine
without either: a missing log file and a refused RCON dial are ordinary states
it reports rather than errors it dies on.
+32
View File
@@ -16,6 +16,7 @@
| `files.${BASE_DOMAIN}` | `62-files.caddy` | static `/srv/files` (read-only, no-auth browse of the share) |
| `distribution.${BASE_DOMAIN}` | `30-distribution.caddy` | static `/srv/distribution` (other repo) |
| `generators.${BASE_DOMAIN}` | `70-generators.caddy` | `generators:80` (offline misode fork + data mirror) |
| `monitor.${BASE_DOMAIN}` | `80-monitor.caddy` | `monitor:8080` (admin log stream + dashboard, **basic_auth**) |
---
@@ -110,9 +111,40 @@ passes it through, so don't rewrite it anywhere in the chain.
---
## Monitor — `monitor.${BASE_DOMAIN}` (admin only)
`reverse_proxy monitor:8080`, behind caddy `basic_auth` (bcrypt,
`MONITOR_USER` / `MONITOR_PASSWORD_HASH`). **The only vhost in the stack that is
gated at caddy**, because the service itself has no auth and publishes no port —
caddy is the sole way in. Per-host on/off via `MONITOR_ENABLED`.
Read-only: every route is a `GET`, and nothing mutates state.
- `GET /` — the whole UI (single HTML file embedded in the binary).
- `GET /api/log[?since=<seq>]` → `{lines[], seq, dropped, reset, total, tail{}}`.
Omit `since` for the full buffer without a spurious "dropped" notice.
`since` is a **sequence number, not a byte offset** — the log rotates on every
deploy. `reset: true` means discard what you have (fell behind, or the service
restarted and your cursor is in the future).
- `GET /api/summary` → all dashboard tiles in one response. Each part is
independently cached and degrades on its own; failures appear as sibling
`*_err` fields next to the last good value.
- `GET /healthz` → `ok`. Liveness only, deliberately not tied to the log file.
Full design + gotchas: `plan/23-monitor.md`.
---
## Internal (service → service, over mcnet)
- `minecraft` → `-javaagent authlib-injector=http://auth.${BASE_DOMAIN}/authlib-injector` (caddy mcnet alias → drasl).
- `nmsr` → `http://auth.${BASE_DOMAIN}` (mojang-compat session/textures/api).
- `mc-status` → `http://drasl:25585/drasl/api/v2` (`DRASL_API_URL`) for the roster; pings `MC_STATUS_TARGET` for live status.
- `mc-backup` → RCON `minecraft:25575`.
- `monitor` → `http://mc-status:8080` (`/v2/status/java/*` + `/players`) for the
status and roster tiles, so it needs no Drasl credentials of its own; RCON
`minecraft:25575` for TPS, restricted to a compile-time allowlist
(`neoforge tps`, `list`) that no HTTP input can reach. Reads
`mc_data:/data:ro` (log + `statfs`) and `./backups:ro` (backup freshness).
- `uptime-kuma` → can probe `http://monitor:8080/healthz` directly, bypassing
caddy, so the monitor vhost needs no unauthenticated path carve-out.