Stream live session-list updates to server browsers #26

Closed
opened 2026-07-15 11:08:22 +02:00 by Kyuubi · 2 comments
Owner

Parent: #1
Depends on: #8, #9, #15
Priority: nice-to-have; not a Rendezvous v1 release gate

Outcome

Let a player leave the server browser open and see sessions appear, change capacity/status, or disappear without manually refreshing or repeatedly replacing the entire list.

Recommended transport

Use Server-Sent Events (SSE) for the first implementation because browser updates are one-way. Keep the existing bounded HTTP snapshot/query API as the source of truth and polling fallback; do not require WebSockets unless a later bidirectional feature justifies them.

Scope

  • Extend the browse snapshot response with a stream revision/cursor.
  • Add a game/environment/filter-scoped SSE endpoint using text/event-stream.
  • Define bounded events:
    • session_upsert — add or replace the public projection of one listing;
    • session_remove — remove a listing that expired, became stale/incompatible/full for the filter, was revoked, or changed visibility;
    • reset — discard local state and fetch a fresh snapshot;
    • keepalive comment/event for idle connections.
  • Give every event a monotonically ordered stream ID suitable for Last-Event-ID reconnect.
  • Retain only a bounded replay window. If a cursor is missing, expired, foreign, or has a sequence gap, emit/return reset rather than silently serving an incomplete view.
  • Add an SDK async stream API that applies events to a caller-owned local projection or returns typed deltas, cancellation, reconnect, and reset outcomes.
  • Coalesce heartbeat/capacity churn over a short configurable interval so the UI updates promptly without flicker or event floods.
  • Keep polling with jittered backoff as fallback for unsupported platforms, restrictive proxies, or repeated stream failures.
  • Preserve all browse filters, tenant/environment/protocol isolation, metadata limits, visibility rules, and endpoint/credential secrecy.
  • Bound subscribers, per-tenant streams, replay memory, queued events, connection duration, keepalive frequency, and slow-consumer behavior.
  • Document reverse-proxy buffering/timeouts and graceful shutdown/reconnect behavior.
  • Keep polished browser UI in each game; Rendezvous supplies contracts, SDK behavior, and TestClient diagnostics only.

Acceptance criteria

  • A snapshot plus subsequent events produces the same public listing projection as a fresh snapshot at the final revision.
  • Create, update, capacity change, UDP-presence staleness/recovery, lease expiry, deregistration, revocation, visibility change, and compatibility change produce the correct delta.
  • Reconnect with a retained Last-Event-ID resumes without duplicate or missing final state.
  • Expired/gapped/invalid cursors force a clean reset and snapshot refresh.
  • Cross-game, environment, protocol, visibility, and filter changes never leak through a stream.
  • Events expose no raw endpoints, lease credentials, capabilities, tickets, secrets, or internal store identifiers.
  • Slow/disconnected consumers cannot grow queues or memory without bound; they are reset or disconnected predictably.
  • Burst tests prove coalescing and subscriber limits bound CPU, memory, bandwidth, and event rate.
  • SDK cancellation/disposal closes the stream and releases all resources.
  • TestClient can display live add/update/remove events and deliberately exercise reconnect/reset behavior.
  • The UI still treats displayed capacity as advisory and join authorization revalidates current state.
  • Polling fallback remains supported and documented.
Parent: #1 Depends on: #8, #9, #15 Priority: nice-to-have; not a Rendezvous v1 release gate ## Outcome Let a player leave the server browser open and see sessions appear, change capacity/status, or disappear without manually refreshing or repeatedly replacing the entire list. ## Recommended transport Use Server-Sent Events (SSE) for the first implementation because browser updates are one-way. Keep the existing bounded HTTP snapshot/query API as the source of truth and polling fallback; do not require WebSockets unless a later bidirectional feature justifies them. ## Scope - Extend the browse snapshot response with a stream revision/cursor. - Add a game/environment/filter-scoped SSE endpoint using `text/event-stream`. - Define bounded events: - `session_upsert` — add or replace the public projection of one listing; - `session_remove` — remove a listing that expired, became stale/incompatible/full for the filter, was revoked, or changed visibility; - `reset` — discard local state and fetch a fresh snapshot; - keepalive comment/event for idle connections. - Give every event a monotonically ordered stream ID suitable for `Last-Event-ID` reconnect. - Retain only a bounded replay window. If a cursor is missing, expired, foreign, or has a sequence gap, emit/return `reset` rather than silently serving an incomplete view. - Add an SDK async stream API that applies events to a caller-owned local projection or returns typed deltas, cancellation, reconnect, and reset outcomes. - Coalesce heartbeat/capacity churn over a short configurable interval so the UI updates promptly without flicker or event floods. - Keep polling with jittered backoff as fallback for unsupported platforms, restrictive proxies, or repeated stream failures. - Preserve all browse filters, tenant/environment/protocol isolation, metadata limits, visibility rules, and endpoint/credential secrecy. - Bound subscribers, per-tenant streams, replay memory, queued events, connection duration, keepalive frequency, and slow-consumer behavior. - Document reverse-proxy buffering/timeouts and graceful shutdown/reconnect behavior. - Keep polished browser UI in each game; Rendezvous supplies contracts, SDK behavior, and TestClient diagnostics only. ## Acceptance criteria - A snapshot plus subsequent events produces the same public listing projection as a fresh snapshot at the final revision. - Create, update, capacity change, UDP-presence staleness/recovery, lease expiry, deregistration, revocation, visibility change, and compatibility change produce the correct delta. - Reconnect with a retained `Last-Event-ID` resumes without duplicate or missing final state. - Expired/gapped/invalid cursors force a clean reset and snapshot refresh. - Cross-game, environment, protocol, visibility, and filter changes never leak through a stream. - Events expose no raw endpoints, lease credentials, capabilities, tickets, secrets, or internal store identifiers. - Slow/disconnected consumers cannot grow queues or memory without bound; they are reset or disconnected predictably. - Burst tests prove coalescing and subscriber limits bound CPU, memory, bandwidth, and event rate. - SDK cancellation/disposal closes the stream and releases all resources. - TestClient can display live add/update/remove events and deliberately exercise reconnect/reset behavior. - The UI still treats displayed capacity as advisory and join authorization revalidates current state. - Polling fallback remains supported and documented.
Kyuubi added reference codex/issue-26-sse-browser-updates 2026-07-16 22:41:10 +02:00
Kyuubi self-assigned this 2026-07-16 22:41:10 +02:00
Author
Owner

Started on codex/issue-26-sse-browser-updates from the verified #23 checkpoint. I will preserve the bounded snapshot API as source of truth, add scoped/bounded SSE replay and reset semantics, expose typed SDK/TestClient behavior, and verify isolation, churn coalescing, slow-consumer bounds, cancellation/disposal, and polling fallback before reconciling the issue.

Started on `codex/issue-26-sse-browser-updates` from the verified #23 checkpoint. I will preserve the bounded snapshot API as source of truth, add scoped/bounded SSE replay and reset semantics, expose typed SDK/TestClient behavior, and verify isolation, churn coalescing, slow-consumer bounds, cancellation/disposal, and polling fallback before reconciling the issue.
Author
Owner

Implemented and verified on codex/issue-26-sse-browser-updates.

Commit: 06c4ecf feat(browser): stream bounded live session updates (#26)

Delivered:

  • filter-bound signed snapshot/stream cursors and bounded SSE session_upsert, session_remove, reset, and keepalive events;
  • ordered retained replay, invalid/expired/foreign/gapped reset behavior, 50 ms coalescing, global/per-tenant subscriber caps, bounded batches, five-minute connections, and no per-subscriber queues;
  • lifecycle coverage for create/update/capacity, presence stale/recovery, lease expiry, deregistration/revocation, visibility/region/protocol transitions, and full-filter isolation;
  • typed SDK async streaming with bounded parsing/open timeout, cancellation/disposal, malformed/oversized envelope failure, and real HTTP Last-Event-ID replay;
  • TestClient watch diagnostics plus deliberate --exercise-reconnect and --exercise-reset flows;
  • generated OpenAPI/public API/golden contract updates, package output, polling fallback and reverse-proxy guidance.

Verification:

  • formatting: clean;
  • Debug build + full tests: 313/313 passed;
  • Release build + full tests: 313/313 passed;
  • focused stream/reset/reconnect/burst/subscriber/parser tests: passed;
  • compatibility policy check: accepted as part of the initial unpublished v1 baseline;
  • Release Client/Contracts NuGet packages: packed successfully;
  • repository whitespace/CLI help smoke: passed.
Implemented and verified on `codex/issue-26-sse-browser-updates`. Commit: `06c4ecf feat(browser): stream bounded live session updates (#26)` Delivered: - filter-bound signed snapshot/stream cursors and bounded SSE `session_upsert`, `session_remove`, `reset`, and `keepalive` events; - ordered retained replay, invalid/expired/foreign/gapped reset behavior, 50 ms coalescing, global/per-tenant subscriber caps, bounded batches, five-minute connections, and no per-subscriber queues; - lifecycle coverage for create/update/capacity, presence stale/recovery, lease expiry, deregistration/revocation, visibility/region/protocol transitions, and full-filter isolation; - typed SDK async streaming with bounded parsing/open timeout, cancellation/disposal, malformed/oversized envelope failure, and real HTTP Last-Event-ID replay; - TestClient `watch` diagnostics plus deliberate `--exercise-reconnect` and `--exercise-reset` flows; - generated OpenAPI/public API/golden contract updates, package output, polling fallback and reverse-proxy guidance. Verification: - formatting: clean; - Debug build + full tests: 313/313 passed; - Release build + full tests: 313/313 passed; - focused stream/reset/reconnect/burst/subscriber/parser tests: passed; - compatibility policy check: accepted as part of the initial unpublished v1 baseline; - Release Client/Contracts NuGet packages: packed successfully; - repository whitespace/CLI help smoke: passed.
Kyuubi changed reference from codex/issue-26-sse-browser-updates to codex/issue-26-sse-browser-updates 2026-07-16 23:26:30 +02:00
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: HeiKyu/Rendezvous#26