52 lines
2.8 KiB
Markdown
52 lines
2.8 KiB
Markdown
# ADR 0006: bounded compatible session browser
|
||
|
||
- Status: Accepted
|
||
- Date: 2026-07-16
|
||
- Tracking: #8
|
||
|
||
## Decision
|
||
|
||
The public list endpoint requires game, environment, and exact gameplay protocol.
|
||
Region is optional, page size is 1–100, and callers may exclude sessions whose
|
||
advisory current-player count has reached the advertised maximum. Lists contain
|
||
public sessions only and only while both lease and authenticated host presence are
|
||
fresh. Unlisted sessions never appear in a list; they may be retrieved directly by
|
||
their 128-bit unguessable listing ID only when the caller also supplies the exact
|
||
game, environment, and protocol scope.
|
||
|
||
Results use ascending opaque listing ID as a deterministic keyset. A cursor carries
|
||
the last ID plus every compatibility/filter field, a five-minute expiry, and an
|
||
HMAC-SHA256 signature under a per-process key. Tampering, expiry, or reuse with a
|
||
different tenant/protocol/region/full filter returns `InvalidRequest`. Restart
|
||
rotates the key, matching the loss of ephemeral listings.
|
||
|
||
Pagination is a bounded live view, not a database snapshot. A record that remains
|
||
eligible and whose ID is greater than the cursor is returned exactly once. Records
|
||
removed or made stale disappear immediately. A record created after a page whose ID
|
||
sorts before that page's cursor is outside that traversal; callers refresh from the
|
||
first page to discover new sessions. This avoids skips or duplicates among stable
|
||
eligible records without retaining per-browser snapshot state.
|
||
|
||
The store reads at most page size plus one record. The service serializes against
|
||
the 256 KiB response ceiling and shortens a page before returning it when metadata
|
||
makes the requested count too large. A continuation cursor is emitted whenever an
|
||
extra or byte-trimmed record remains. All cursor, page, metadata, property, scalar,
|
||
and collection sizes are bounded before untrusted allocation can grow without a
|
||
ceiling.
|
||
|
||
Browser DTOs are fresh copies containing only opaque listing ID, exact compatibility,
|
||
region, visibility/trust presentation, advisory capacity, build/display labels, and
|
||
policy-validated string metadata. They contain no observed endpoint, lease,
|
||
capability, ticket, credential fingerprint, derivation salt, principal subject, or
|
||
store key. Metadata is display text: JSON encoding escapes markup, but game UI must
|
||
still render values as text and must never execute markup, interpret endpoints, or
|
||
use metadata for authorization.
|
||
|
||
## Consequences
|
||
|
||
- Cross-game, cross-environment, incompatible, stale, revoked, expired, unlisted,
|
||
and optionally full sessions are removed before response construction.
|
||
- Direct unlisted lookup is suitable for an out-of-band invite carrying the opaque
|
||
ID; human join codes remain future work and require their own bounded abuse model.
|
||
- Host capacity remains advisory. The host makes the final admission decision.
|