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:
1 parent
bc99427ef9
commit
bac2372c95
5 files changed
+119
-4
No files matched your search
@@ -37,6 +37,8 @@ Internet ── host nginx (TLS, LE certs, HSTS) ──► caddy (127.0.0.1:8880
|
||||
upload.${BASE_DOMAIN} ─► filestash (player file share, ONE shared login, writable)
|
||||
files.${BASE_DOMAIN} ─► caddy (read-only, no-auth browse of the same share dir;
|
||||
per-host on/off via FILES_BROWSE — off on cochi)
|
||||
monitor.${BASE_DOMAIN}─► monitor (ADMIN ONLY: latest.log stream + dashboard tiles,
|
||||
caddy basic_auth; per-host on/off MONITOR_ENABLED)
|
||||
distribution.${BASE} ─► static site from ANOTHER repo (DISTRIBUTION_WEB_ROOT)
|
||||
|
||||
minecraft :25565 (+ 24454/udp Simple Voice Chat)
|
||||
@@ -153,6 +155,34 @@ works. After any filestash config change, check the browser-facing values
|
||||
the broken redirect announced itself there (`msg=Redirecting to http://https://`)
|
||||
while every other check passed.
|
||||
|
||||
### Compose eats `$` in `.env` values — quote every hash
|
||||
`docker compose` interpolates `$`-sequences inside `.env` **values**, so a bare
|
||||
bcrypt hash is silently corrupted before it ever reaches a container:
|
||||
|
||||
```
|
||||
MONITOR_PASSWORD_HASH=$2a$14$abcdefgHIJKLmnop.qrstuv # WRONG
|
||||
→ container sees $2a$14.qrstuv # $abcdefg… eaten as an unset var
|
||||
MONITOR_PASSWORD_HASH='$2a$14$abcdefgHIJKLmnop.qrstuv' # single quotes fix it
|
||||
```
|
||||
|
||||
Verified, not folklore. Every login fails with no hint as to why — the hash
|
||||
looks fine in `.env` and `docker compose config` re-escapes it to `$$…`, which
|
||||
looks wrong but is correct. **The same bug bites over SSH**: writing a hash to a
|
||||
remote `.env` through `ssh host "...$HASH..."` lets the remote shell expand it.
|
||||
Transport it base64-encoded and verify with `md5sum` on both ends.
|
||||
|
||||
Note the two services want *opposite* formats: caddy `basic_auth` needs
|
||||
**bcrypt** (`caddy hash-password`), filestash needs **`$6$`** (`openssl passwd
|
||||
-6`) and silently rejects bcrypt. Never copy a hash between them.
|
||||
|
||||
### Monitor runs as root on purpose
|
||||
`/data` is `drwxr-x---` owned by uid 1000 (itzg). A non-root container user
|
||||
cannot traverse it, so the log tailer would read nothing — **silently**, while
|
||||
still answering HTTP 200. Adding a `USER` directive to `docker/monitor/` as
|
||||
"hardening" breaks it. What actually bounds the risk there: every mount is `:ro`,
|
||||
no port is published, and the one credential it holds (RCON) can only send
|
||||
commands from a compile-time allowlist.
|
||||
|
||||
### Memory sizing
|
||||
~50–100 mods, 8 players, 1.21.1: `INIT_MEMORY: 4G`, `MAX_MEMORY: 10G`,
|
||||
`USE_AIKAR_FLAGS: TRUE`. **The pack is now 164 jars / 133 server-side** (Create
|
||||
@@ -219,6 +249,10 @@ Full design settled across WS1–WS6; all tasks unchecked, ready to execute.
|
||||
htpasswd login, writable, rendered `:ro` config. See `plan/20-files.md`.
|
||||
Follow-ups: add the Uptime Kuma HTTP monitor for `files.`; the share is
|
||||
**outside mc-backup** and unquota'd (accepted — eyeballed).
|
||||
- [x] **Monitor** (`monitor.`, 2026-08-01) — admin-only `latest.log` stream +
|
||||
dashboard tiles (status, TPS via allowlisted RCON, players, disk, backups,
|
||||
joins/leaves). Live on cochi, all three phases done. See `plan/23-monitor.md`.
|
||||
Follow-up: add the Uptime Kuma monitor for `http://monitor:8080/healthz`.
|
||||
- [ ] **Verify NeoForge version** against current stable before first deploy.
|
||||
- [x] **Bootstrap Drasl admin** account (`admin`, 2026-06-10). Key is
|
||||
`DefaultAdmins` (not `DefaultAdminUsernames`); first admin needs an invite-flip.
|
||||
|
||||
Reference in new issue
Block a user