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
@@ -19,6 +19,7 @@ One compose file, one `docker compose up -d`:
|
||||
| `uptime-kuma` | louislam/uptime-kuma | status page at `status.${BASE_DOMAIN}` |
|
||||
| `filestash` | machines/filestash (digest-pinned) | player file share (upload) at `upload.${BASE_DOMAIN}` |
|
||||
| `mc-backup` | itzg/mc-backup | world backups every 6h via RCON |
|
||||
| `monitor` | built from source | admin-only log stream + dashboard at `monitor.${BASE_DOMAIN}` |
|
||||
|
||||
Vhosts (all proxied through the host nginx → caddy):
|
||||
|
||||
@@ -27,6 +28,7 @@ Vhosts (all proxied through the host nginx → caddy):
|
||||
- `avatar.` — NMSR
|
||||
- `status.` — Uptime Kuma
|
||||
- `files.` — Filestash player file share (one shared login, writable)
|
||||
- `monitor.` — **admin only**, log stream + dashboard (caddy basic_auth)
|
||||
- `distribution.` — static site from another repo (`DISTRIBUTION_WEB_ROOT`)
|
||||
|
||||
Config lives in `.env`: `BASE_DOMAIN`, `RCON_PASSWORD`, `CADDY_HTTP_PORT` /
|
||||
@@ -35,7 +37,8 @@ Config lives in `.env`: `BASE_DOMAIN`, `RCON_PASSWORD`, `CADDY_HTTP_PORT` /
|
||||
## DNS
|
||||
|
||||
Handled **outside this repo**. Point `${BASE_DOMAIN}` and every subdomain
|
||||
(`auth. avatar. status. files. distribution. www.`) at the host running nginx.
|
||||
(`auth. avatar. status. files. monitor. distribution. www.`) at the host running
|
||||
nginx.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -158,6 +161,43 @@ curl -H "Host: ulicraft.net" \
|
||||
|
||||
Wired in `landing/src/data/site.ts` (`statusApi`) and `caddy/conf.d/10-static.caddy`.
|
||||
|
||||
## Monitor (admin log stream + dashboard)
|
||||
|
||||
`monitor.${BASE_DOMAIN}` — a small Go service (`docker/monitor/`) that tails the
|
||||
Minecraft server's `latest.log` and shows it live with autoscroll, a filter and
|
||||
ERROR/WARN/INFO chips, above a row of tiles: server status, TPS, players online
|
||||
(with NMSR heads), registered accounts, disk free, backup freshness and recent
|
||||
joins/leaves.
|
||||
|
||||
**Admin only, and read-only.** The log carries player IPs, chat and admin
|
||||
commands verbatim and nothing sanitizes it, so the whole vhost sits behind caddy
|
||||
`basic_auth`. There are no controls in the UI — every route is a `GET`.
|
||||
|
||||
```bash
|
||||
# generate the credential (BCRYPT — the opposite of FILES_PASSWORD_HASH, which needs $6$)
|
||||
docker run --rm caddy:alpine caddy hash-password --plaintext 'your-password'
|
||||
```
|
||||
|
||||
Then in `.env` — **single-quote the hash**, Compose mangles bare `$`-sequences:
|
||||
|
||||
```sh
|
||||
MONITOR_ENABLED=on # per-host on|off, like FILES_BROWSE
|
||||
MONITOR_USER=admin
|
||||
MONITOR_PASSWORD_HASH='$2a$14$...'
|
||||
```
|
||||
|
||||
The container publishes **no port**: caddy is the only way in, so there is no
|
||||
host listener to firewall. It holds one credential (`RCON_PASSWORD`, for the TPS
|
||||
tile) and can only send commands from a compile-time allowlist that no HTTP
|
||||
input can reach.
|
||||
|
||||
Adding it needs a cert (`monitor` in `LE_SUBDOMAINS` →
|
||||
`tooling/issue-letsencrypt.sh`) and an nginx vhost (`tooling/render-nginx.sh
|
||||
--install`). Because it adds a caddy conf file, the **first** deploy needs
|
||||
`./update-all.sh --hard` (world downtime); later changes are rolling.
|
||||
|
||||
Full design, gotchas and verification: **`plan/23-monitor.md`**.
|
||||
|
||||
## Player file share (Filestash)
|
||||
|
||||
`upload.${BASE_DOMAIN}` is a web file share for player media — screenshots,
|
||||
@@ -224,6 +264,8 @@ caddy/Caddyfile + conf.d/*.caddy # ingress vhost snippets
|
||||
drasl/config/config.toml.tmpl # auth config (rendered)
|
||||
nmsr/config.toml.tmpl # avatar renderer config, Drasl-backed (rendered)
|
||||
docker/nmsr/ # built-from-source NMSR image
|
||||
docker/mc-status/ # built-from-source SLP→JSON pinger
|
||||
docker/monitor/ # built-from-source admin log stream + dashboard
|
||||
landing/ # Astro site → www/
|
||||
launcher/ # FjordLauncher assets (gitignored)
|
||||
server-mods/ # filtered server mod jars (synced, gitignored)
|
||||
|
||||
Reference in new issue
Block a user