Interrupted, not gone
PvP duel players get an advisory signal for a stalled opponent, built on an authentication boundary the old unauthenticated stream design never needed, and a hard line against ever letting that signal decide the game.
A player mid-duel with a stalled opponent has always had exactly one piece of information: the turn timer, counting down toward the same three-timeout auto-forfeit whether the other side is thinking, has a bad connection, or has closed the tab. Nothing distinguished those cases. The fix is an advisory connection-status signal, present or interrupted, shown as a transient notice and nothing more, built on top of a stream design that was never meant to carry that kind of trust.
The existing PvP event stream sent thin wakeups, a client refetches the authoritative game state whenever one arrives, and it was safe to leave those wakeups unauthenticated because nothing about them claimed to be true. A role a client asserted for itself was fine when the worst outcome was one extra harmless refetch. Attributing a live connection to a specific player is a different claim entirely, and an unauthenticated client cannot be trusted to make it. EventSource cannot attach a custom header or bearer token to its request, which ruled out attaching the existing game token to the stream connection directly. The design that survived research into the alternatives mints a credential first: the client exchanges its game token through a same-origin request, the server derives the role itself rather than trusting what the client claims, and only then does the browser open the stream, carrying the resulting cookie automatically because native EventSource always sends cookies.
if (request.headers.get('origin') !== url.origin) throw error(403, 'Forbidden');
// ...token validated, rate limit checked...
cookies.set(STREAM_COOKIE, pvpPresence.issue(gameId, role), {
httpOnly: true,
secure: true,
sameSite: 'strict'
// short-lived, scoped to this game's stream path
});
return new Response(null, { status: 204 });
A signal that can be wrong has to stay advisory, on purpose, everywhere it touches the game.
That distinction shaped the language as much as the mechanics. The status is exactly two states, connected and connection-interrupted, and deliberately not “left” or “offline” or “disconnected,” words that claim more certainty than a dropped transport actually gives. A cold server process that has not yet observed either player reports unknown rather than guessing interrupted, and multiple open tabs for the same player count as present until every one of them closes, so switching devices mid-duel does not read as an interruption either. Verifying all of that without slow real-world waits meant testing the grace timer, the multi-tab aggregation, and the recovery path against fake clocks, saving real two-tab browser races for the handful of scenarios that actually needed them.
The notice itself reused the existing overlay shell that already carried reward and hint notices rather than inventing a fourth kind of UI, picking up a status role with its own icon and dwell time alongside the roles already there. Separately, and deliberately kept out of this change, the documentation still claims a duel-word limit and PvP collision rules that no longer match how the game actually ends a match. That cleanup was scoped out on purpose, left for its own pass once the connection-status work landed.