Files
Rendezvous/docs/architecture/0005-session-lease-lifecycle.md
T
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

94 lines
4.8 KiB
Markdown

# 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.
```mermaid
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.