# Minecraft Server Live Log Viewer A read-only Axum + Svelte sidecar for `itzg/docker-minecraft-server`. It streams `latest.log` using filesystem events, paginates plain/gzip/tar-gzip history, and permits an IP only after a currently whitelisted player successfully joined from that address. ## Run as a Docker sidecar 1. Copy `compose.example.yml` beside the Minecraft Compose project. 2. Replace `PUBLIC_ORIGIN`, `TRUSTED_PROXY_CIDRS`, and the external `caddy` network name. 3. Run `docker compose -f compose.example.yml up -d --build`. 4. Point Caddy at `log-viewer:8080`; do not publish the viewer port to the host. The container runs as UID/GID `10001`, uses a read-only root filesystem, drops every capability, does not mount the Docker socket, and mounts the shared Minecraft volume at `/data:ro`. The Compose example bind-mounts the host directory `log-viewer-config` and reads `log-viewer-config/ip-whitelist.txt`. Put one IP address or CIDR on each line (`#` comments are supported) to grant an explicit owner override. Mount the directory rather than the individual file: editors commonly save by replacing a file, and an individual Docker file bind mount remains attached to the replaced inode. The file is read after normal player authorization fails on every access attempt, so edits take effect without restarting the viewer. An unreadable or malformed file grants no override. With `RUST_LOG=minecraft_log_viewer=debug,tower_http=info`, each API access logs the effective client IP and authorization source. Override checks also log the configured path, complete parsed IP/CIDR list, match/miss result, and read or parse errors. The complete set of IPs authorized through successful joins and the current Minecraft whitelist is logged as `allowed_player_ips`. Example Caddy integration: ```caddyfile logs.example.com { reverse_proxy log-viewer:8080 } ``` Caddy supplies `X-Forwarded-For`; the service reads it only when the immediate peer belongs to `TRUSTED_PROXY_CIDRS`. Set this CIDR to the actual Caddy Docker network. Direct clients cannot spoof authorization using that header. ## Authorization lifetime Only successful joins in the current `latest.log` count. Failed, banned, rejected, and archived attempts do not. Associations survive disconnects until the current log is replaced/truncated or the viewer restarts. A whitelist removal revokes access after the watched JSON file is reloaded. This is a lightweight IP-based privacy barrier, not account authentication; NATs, VPNs, dynamic addresses, and IPv6 privacy addresses limit its precision. ## Configuration Required: `PUBLIC_ORIGIN`. Paths default to `/data`, `/data/logs`, `/data/logs/latest.log`, and `/data/whitelist.json`. Set optional `IP_WHITELIST_FILE` to a mounted owner-managed IP/CIDR file. The Compose example documents proxy and redaction settings. Resource limits are configurable with `INITIAL_LOG_LINES`, `MAX_HISTORY_LINES_PER_REQUEST`, `ARCHIVE_CACHE_MAX_BYTES`, `ARCHIVE_CACHE_MAX_FILES`, `MAX_ARCHIVE_DECOMPRESSED_BYTES`, `WS_CLIENT_QUEUE_CAPACITY`, and `MAX_WS_CONNECTIONS`. Malformed startup configuration or an unreadable initial whitelist fails closed. Later malformed whitelist updates retain the last valid snapshot. ## Development and verification ```sh cargo test cd frontend && npm install && npm test && npm run check && npm run build docker build -t minecraft-log-viewer . ``` No Node runtime, database, Redis service, recurring file poller, Docker API, RCON, or writable Minecraft mount is used in production.