diff --git a/.labyricorn/devlog/private-admin-console-and-graceful-disconnects/contents.lr b/.labyricorn/devlog/private-admin-console-and-graceful-disconnects/contents.lr new file mode 100644 index 0000000..260d457 --- /dev/null +++ b/.labyricorn/devlog/private-admin-console-and-graceful-disconnects/contents.lr @@ -0,0 +1,62 @@ +_model: devlog-entry +--- +schema_version: 1 +--- +title: Private operations arrive with graceful viewer disconnects +--- +date: 2026-08-17 +--- +author: Codex and Christopher Chambers +--- +summary: Twungeon gained a private, authenticated operator console and last-socket disconnect handling that sends absent viewers directly into AutoGuard instead of delaying every player phase. +--- +tags: implementation, deployment, operations, admin panel, networking, security, multiplayer, AutoGuard, testing +--- +source_commit: 8b1d14dceac8f9645f70cab106a045ed64bcd71c +--- +body: + +Live multi-viewer testing revealed an awkward failure mode: when a viewer left +the channel, their character remained in the dungeon with unspent action points. +Keeping the character was intentional because a refresh or temporary network +failure should not destroy a viewer's progress, but waiting for its action-point +deadline made each later player phase appear stuck. + +Twungeon now tracks authenticated WebSocket connections per Twitch user. A +viewer is marked disconnected only after their final socket closes, so a second +tab remains valid and closing one of several connections does not incorrectly +end the turn. On the last close, unused action points immediately become Guard. +Extension-bound characters that remain disconnected also enter AutoGuard as +each later phase starts. Their characters stay on the board for a safe +reconnection while connected viewers can complete the phase normally. + +The same milestone adds a private operator console for live troubleshooting. +It runs on a separate listener rather than the public Twungeon origin. The +production listener binds specifically to `10.138.4.44:3001`, and the +application rejects source addresses outside `10.138.0.0/16`. Basic +authentication, a unique generated deployment password, per-process form +tokens, no-store responses, a restrictive Content Security Policy, and frame +denial provide additional boundaries. The port is not exposed through the +public web listener or reverse proxy. + +The server-rendered console shows the current run, floor, phase, players, +Twitch readiness, OAuth authorization, uptime, and the latest 40 action-log +entries. An operator can disconnect a viewer into AutoGuard, remove a character, +end the active player phase, or reset the run. These operations call the same +authoritative game core used by viewer commands and broadcast the resulting +snapshot to connected clients. + +Configuration remains opt-in. The admin listener will not start unless it is +explicitly enabled with a `10.138.x.x` bind address and a password of at least +16 characters. Operator documentation records the environment settings, +network boundary, authentication flow, and the requirement not to publish or +proxy the private port. + +Validation covered linting, strict type checking, a production build, 28 domain +tests, and 13 integration and end-to-end tests. Deployment checks confirmed +that the public service and Twitch adapter were ready, authenticated admin +health succeeded on the private address, unauthenticated access was rejected, +and the admin port was not listening on loopback. + +The private operations and graceful-disconnect implementation is recorded in +[commit `8b1d14dceac8f9645f70cab106a045ed64bcd71c`](https://git.labyricorn.com/Labyricorn/Twungeon/commit/8b1d14dceac8f9645f70cab106a045ed64bcd71c).