Files
Rendezvous/docs/architecture/0005-session-lease-lifecycle.md
KyuubiYoru 6d076c281a
quality-gate / quality (push) Successful in 59s
feat: implement authenticated NAT mediator (#11)
Closes #11
2026-07-16 07:37:02 +02:00

4.8 KiB

ADR 0005: authenticated session lease and presence lifecycle

  • Status: Accepted
  • Date: 2026-07-16
  • Tracking: #7

Context

A host needs to publish a player-facing session without letting an HTTP request claim a public endpoint or remain visible after the gameplay socket disappears. Registration retries must be safe, credentials must remain opaque, and policy or ownership checks cannot race state mutation.

Decision

The four host HTTP operations require Authorization: Bearer <publisher credential>. The signed principal supplies the authoritative game, environment, publisher trust mode, subject, and allowed regions. Request fields never widen that scope. Creation and update apply the enabled GamePolicy to exact protocol, region, visibility, bounded display/build/capacity values, and the allowlisted metadata schema.

Capacity reported by a host is advisory directory information. Rendezvous bounds and publishes it but never treats it as final admission authority; the game host still decides identity, bans, reserved slots, and whether a connection may join.

stateDiagram-v2
    [*] --> AwaitingPresence: authorized register
    AwaitingPresence --> Listed: valid host UDP presence
    Listed --> AwaitingPresence: presence deadline passes
    AwaitingPresence --> AwaitingPresence: lease renew or data update
    Listed --> Listed: lease renew, data update, or presence refresh
    AwaitingPresence --> Removed: lease expiry or delete
    Listed --> Removed: lease expiry or delete
    Removed --> [*]

Registration returns a listing ID, lease ID/token, host-presence handle/capability, lease expiry, a 30-second renewal suggestion, and a 10-second presence-refresh suggestion. The authoritative ceilings remain 60 seconds for the lease and 20 seconds for presence. Timing suggestions are server-controlled, not client-selected.

The lease token and presence capability are 256-bit opaque values derived with HMAC-SHA256 from an in-memory per-process secret, a purpose label, the publisher subject, the idempotency key, a canonical request fingerprint, and a random per-registration derivation salt. Opaque IDs use separate purpose labels. Exact retries read the retained non-secret salt and therefore reproduce the original response without retaining plaintext credentials. Once the bounded idempotency record expires, a new salt rotates IDs and capabilities so an old token cannot regain authority. Metadata order is canonicalized before fingerprinting. The store retains the salt and only a second keyed fingerprint of each token. Restart rotates the derivation secret while the matching ephemeral state disappears.

Renew, update, and delete require both the same publisher subject and the lease capability. Cross-owner or wrong-capability access returns the same not-found shape. Update may change display name, build label, advisory capacity, and metadata only; game, environment, region, protocol, visibility, trust mode, and opaque IDs remain canonical. Delete is idempotent and does not reveal whether another publisher owns the supplied ID.

UDP presence

Only a structurally valid frozen HostPresence envelope or native LiteNetLib host-presence request with the issued capability can refresh presence. The public endpoint is the UDP packet's observed source on the host's gameplay socket; the HTTP API never accepts one. The bounded local candidate comes from the authenticated packet. Invalid or unknown inputs receive no response. ADR 0009 defines the later attempt-role use of frozen ClientPresence and native host/client requests. Presence expiry demotes public visibility but keeps the lease, so the same handle can restore visibility without changing session identity.

Public listing responses contain bounded listing data only. They never contain public/local endpoints, lease tokens, presence capabilities, fingerprints, store keys, or canonical player identity.

Failure semantics

  • malformed or policy-invalid fields return a stable typed InvalidRequest;
  • an unsupported gameplay protocol returns IncompatibleProtocol;
  • missing/invalid publisher authentication returns AuthenticationRequired;
  • cross-scope authorization returns Forbidden without resource disclosure;
  • wrong owner/capability or expired state returns the tenant-hidden NotFound;
  • idempotency reuse with changed input returns Conflict;
  • publisher/global exhaustion returns CapacityExceeded; and
  • drain or loss of atomic state returns ServiceUnavailable and authorizes no join.

Consequences

  • HTTP registration alone can never make a public session browseable.
  • Plaintext session capabilities are returned to the intended host but are not retained, logged, included in public listing DTOs, or exported as metrics.
  • Re-registration after restart is the recovery path; there is no durable session identity or gameplay state in Rendezvous.