feat(monitor): admin-only latest.log stream at monitor.
Phase 1 of plan/23-monitor.md: a small Go service that tails the Minecraft server's latest.log into an in-memory ring buffer and serves it to an embedded single-page UI (autoscroll, substring/regex filter, level chips). Read-only by construction — /data is mounted :ro, there is no mutating route, and the container holds no credentials. It publishes NO port: caddy's basic_auth vhost is the only way in, which is the point, since the service has no auth of its own and the log carries player IPs, chat and admin commands. Two non-obvious choices, both to avoid prod-only failures: - Polling, not SSE. The chain is host nginx -> caddy -> app, and nginx buffers proxied responses by default, so an EventSource would stall on cochi (whose vhost is an untracked per-host file) and not reproduce locally. Delay was accepted at design time, so polling is strictly better. - The cursor is a sequence number, not a byte offset, and the tailer reopens on inode change or size shrink. itzg rotates latest.log on every server restart, i.e. every deploy — a naive tailer goes silent afterwards while still answering HTTP 200. Verified against rotate, truncate, delete/recreate, partial-line and ring-overflow cases. MONITOR_ENABLED (on|off) gates both layers per host, mirroring FILES_BROWSE. MONITOR_PASSWORD_HASH must be bcrypt AND single-quoted in .env: Compose interpolates $-sequences in .env values, so a bare hash is silently mangled ($2a$14$abc... -> $$2a$$14...) and every login fails with no hint why. Confirmed empirically, and it is the opposite of filestash, which needs $6$. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
8507ffd020
commit
fca6747bcc
13 files changed
+1241
-4
No files matched your search
+32
-1
@@ -134,11 +134,42 @@ GENERATORS_DATA_DIR=./generators-data
|
||||
# Only needed if using CurseForge-exclusive mods:
|
||||
# CF_API_KEY=your-curseforge-api-key
|
||||
|
||||
# ── Monitor (monitor.${BASE_DOMAIN}) — admin log stream ───────────────
|
||||
# Read-only view of the Minecraft server's latest.log. ADMIN ONLY: the log
|
||||
# carries player IPs, chat and admin commands verbatim, and nothing sanitizes
|
||||
# it. Full design + gotchas: plan/23-monitor.md.
|
||||
#
|
||||
# Per-host toggle, `on` | `off` (default on), same two-layer gating as
|
||||
# FILES_BROWSE: the caddy conf mount (docker-compose.yml substitutes the value
|
||||
# into the path — 80-monitor.on.caddy vs the .off.caddy stub) and the public
|
||||
# nginx vhost (tooling/render-nginx.sh). `off` also drops the name from the
|
||||
# port-80 redirect, so no cert is needed — drop `monitor` from LE_SUBDOMAINS
|
||||
# too on that host.
|
||||
# After changing it: tooling/render-nginx.sh --install
|
||||
# docker compose up -d --force-recreate caddy
|
||||
MONITOR_ENABLED=on
|
||||
|
||||
# The ONLY credential guarding the log stream — caddy basic_auth. The monitor
|
||||
# container has no auth of its own and publishes no port, so this is the whole
|
||||
# access control.
|
||||
MONITOR_USER=admin
|
||||
#
|
||||
# MUST be bcrypt, generated with:
|
||||
# docker run --rm caddy:alpine caddy hash-password --plaintext 'your-password'
|
||||
# (This is the OPPOSITE of FILES_PASSWORD_HASH, which must be $6$ and silently
|
||||
# rejects bcrypt. Do not reuse a hash between the two.)
|
||||
#
|
||||
# MUST be SINGLE-QUOTED here. Docker Compose interpolates $-sequences in .env
|
||||
# values, so an unquoted bcrypt hash is silently mangled — $2a$14$abcdefg…
|
||||
# becomes $$2a$$14… with the $abcdefg… segment eaten as an unset variable, and
|
||||
# every login fails with no hint as to why. Verified behaviour, not a guess.
|
||||
MONITOR_PASSWORD_HASH='$2a$14$replace.me.with.a.real.bcrypt.hash'
|
||||
|
||||
# ── Let's Encrypt via OVH DNS-01 (tooling/issue-letsencrypt.sh) ───────
|
||||
# Only needed to issue real TLS certs. DNS-01 needs no inbound ports.
|
||||
LE_EMAIL=you@example.com
|
||||
# space-separated subdomains; apex ${BASE_DOMAIN} is always included
|
||||
LE_SUBDOMAINS=auth distribution www avatar status upload files generators
|
||||
LE_SUBDOMAINS=auth distribution www avatar status upload files generators monitor
|
||||
# OVH API creds (DNS zone write). Create at https://eu.api.ovh.com/createToken/
|
||||
# OVH_CK can be blank on first run — acme.sh prints an auth URL, then paste it.
|
||||
OVH_END_POINT=ovh-eu
|
||||
|
||||
Reference in new issue
Block a user