112 lines
5.2 KiB
Markdown
112 lines
5.2 KiB
Markdown
# 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. Contract-only endpoints
|
||
return `501` until their behavior is implemented by the subsequent directory,
|
||
lease, and join-orchestration issues.
|
||
|
||
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.
|
||
|
||
## 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 | 1–10,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`, `capacityExceeded` |
|
||
| 410 | `expired`, `staleHost` |
|
||
| 429 | `rateLimited` (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.
|