Files
Rendezvous/docs/contracts/http-v1.md
T
KyuubiYoru 94aba8a3bb
quality-gate / quality (push) Successful in 59s
feat(client): standardize connection outcomes (#13)
2026-07-16 10:18:41 +02:00

128 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HTTP contract v1
Tracking: #4
All production endpoints require HTTPS. JSON uses UTF-8, camel-case property
names, compact output, string-valued camel-case enums, and ISO 8601 timestamps.
Every request that contains a body carries `contractVersion: 1`; browse carries
the same value as a required query parameter.
## Compatibility and parsing
- Contract version matching is exact. Any value other than `1` fails with
`unsupportedContractVersion`; it is never guessed or downgraded.
- Gameplay protocol matching is exact. `buildVersion` is display and diagnostic
text only and never decides compatibility.
- Unknown JSON object properties are ignored so additive v1 responses remain
readable. Unknown enum names, numeric enum values, comments, trailing commas,
invalid identifier strings, and excessive nesting are rejected.
- Game, environment, and region IDs are lowercase URL-safe slugs. Listing,
lease, join-attempt, and mediation IDs are non-empty UUIDs serialized as JSON
strings.
- Clients must honor request cancellation. A disconnected or cancelled request
does not promise a response body; the server should stop work where safe.
## Endpoints
| Method | Path | Purpose |
| --- | --- | --- |
| `POST` | `/v1/sessions` | Register a session and create its renewable lease. |
| `POST` | `/v1/sessions/{listingId}/renew` | Renew the listing lease. |
| `PUT` | `/v1/sessions/{listingId}` | Replace mutable browser fields and capacity. |
| `DELETE` | `/v1/sessions/{listingId}` | Withdraw a listing. |
| `GET` | `/v1/sessions` | Browse compatible public sessions. |
| `GET` | `/v1/sessions/{listingId}` | Resolve a public or explicitly shared unlisted listing. |
| `POST` | `/v1/join-attempts` | Authorize and create a short-lived join attempt. |
| `DELETE` | `/v1/join-attempts/{attemptId}` | Cancel an attempt using its client punch capability. |
| `GET` | `/v1/sessions/{listingId}/join-attempts` | Let an authenticated host poll pending attempts. |
| `POST` | `/v1/join-attempts/{attemptId}/outcome` | Report a bounded connection outcome. |
| `GET` | `/health/live` | Report that the HTTP process is alive. |
| `GET` | `/health/ready` | Report whether the UDP mediator is bound and ready. |
The generated [OpenAPI document](../api/rendezvous-v1.json) is the normative
shape reference for parameters, bodies, and responses.
Host polling sends its reusable lease credential in
`X-Rendezvous-Lease-Token`; it must never be placed in a URL. Lease credentials
for mutation operations are carried in their request bodies. Public browser
responses contain no IP endpoints, lease tokens, punch capabilities, connection
tickets, player identifiers, or gameplay state.
Attempt cancellation sends the short-lived client punch capability in
`X-Rendezvous-Client-Punch-Capability`. Join creation uses the observed HTTP
source only for a process-keyed, short-lived idempotency/abuse scope; this is not
player authentication and is never returned to callers.
Outcome reporting uses that same short-lived capability. It accepts only outcomes
for an issued attempt and carries one stable outcome enum plus one coarse elapsed
bucket. Exact duplicate reports are idempotent; conflicting repeats fail. Reports
never carry exact timing, diagnostics, endpoints, metadata, player identifiers,
or credentials.
The frozen v1 .NET request also retains deprecated `elapsedMilliseconds` and
`diagnosticCode` properties for source/wire compatibility. Current clients omit
them. If a legacy client supplies them, the server immediately converts elapsed
milliseconds to the coarse bucket and discards diagnostic text; neither value is
retained or used as a metric dimension.
Registration and update may include one validated `dedicatedFallback`. The
endpoint must be enabled by the tenant's provisioned fallback policy, is visible
browser data, and is copied into subsequently issued attempts.
It is a handoff for caller-owned policy: neither the HTTP service nor the SDK
automatically connects to it. V1 provides no gameplay relay.
## Idempotency, cursors, and retries
Registration and join creation require a caller-generated visible-ASCII
`idempotencyKey`. A repeat in the same authorization scope returns the original
result while the key is retained; reusing a key with a different payload fails
with `conflict`. Keys are opaque and must not contain credentials.
Cursors are opaque, endpoint-specific, short-lived values. A client may echo a
cursor only to the endpoint and filters that produced it. Invalid or expired
cursors fail with `invalidRequest`; clients restart browsing from the first page.
Renew, update, delete, and outcome reporting are safe to retry with the same
lease/attempt identity after a transport-level failure.
## Limits
Limits are measured after UTF-8 encoding where stated. Servers reject the
entire request rather than truncate values.
| Item | v1 limit |
| --- | ---: |
| HTTP request body | 16 KiB |
| Browser response body | 256 KiB |
| Browser page | 100 listings |
| Metadata document | 4 KiB, 32 keys |
| Metadata key / value | 64 / 256 UTF-8 bytes |
| Game / environment / region ID | 64 / 32 / 32 characters |
| Display name / build version | 128 / 64 UTF-8 bytes |
| Idempotency key | 64 visible ASCII characters |
| Cursor | 512 visible ASCII characters |
| Diagnostic code | 64 visible ASCII characters |
| Error message | 256 UTF-8 bytes |
| Reusable HTTP credential | 1,024 characters |
| Session capacity | 110,000 players |
## Error mapping
Errors use `ApiError` with a stable `code`, bounded safe `message`, optional
`correlationId`, and optional `retryAfterSeconds`. Messages are diagnostic and
must not be parsed. Secrets and raw credentials are never echoed.
| HTTP | Codes |
| ---: | --- |
| 400 | `invalidRequest`, `unsupportedContractVersion` |
| 401 | `authenticationRequired` |
| 403 | `forbidden` |
| 404 | `notFound` |
| 409 | `conflict`, `incompatibleProtocol`, `replayRejected` |
| 410 | `expired`, `staleHost` |
| 429 | `rateLimited`, `capacityExceeded` (with retry guidance when known) |
| 503 | `serviceUnavailable` (with retry guidance when known) |
| 500 | `internalError` |
Malformed input must receive the same bounded error family regardless of which
parser or validation stage rejected it.