Files
x0gp/server/docs/docs.go
T

3388 lines
121 KiB
Go

// Package docs Code generated by swaggo/swag. DO NOT EDIT
package docs
import "github.com/swaggo/swag"
const docTemplate = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "{{escape .Description}}",
"title": "{{.Title}}",
"contact": {
"name": "x0gp"
},
"license": {
"name": "MIT"
},
"version": "{{.Version}}"
},
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/api/cars": {
"get": {
"description": "Returns all cars visible to the caller. Optional query filters: ` + "`" + `scope=system|public|private` + "`" + `, ` + "`" + `owner_id=\u003cstring\u003e` + "`" + `.",
"produces": [
"application/json"
],
"tags": [
"cars"
],
"summary": "List cars",
"parameters": [
{
"enum": [
"system",
"public",
"private"
],
"type": "string",
"description": "Filter by visibility",
"name": "scope",
"in": "query"
},
{
"type": "string",
"description": "Filter by owner id",
"name": "owner_id",
"in": "query"
}
],
"responses": {
"200": {
"description": "List of cars with count",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"post": {
"description": "Creates a new car. The server fills ` + "`" + `id` + "`" + ` if the client sends an empty one. ` + "`" + `owner_id` + "`" + ` should be the caller's id in production (auth is disabled in PoC).",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"cars"
],
"summary": "Create a car",
"parameters": [
{
"description": "Car to create",
"name": "car",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/transport.CarCreateRequest"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/transport.CarAck"
}
},
"400": {
"description": "Invalid input",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"409": {
"description": "Car id already exists",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/cars/{id}": {
"get": {
"description": "Dispatches on HTTP method. The id is taken from the URL path (` + "`" + `/api/cars/{id}` + "`" + `). System cars are immutable and cannot be updated or deleted.",
"produces": [
"application/json"
],
"tags": [
"cars"
],
"summary": "Get / update / delete a car by id",
"parameters": [
{
"type": "string",
"description": "Car id",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Patch payload (only for PUT)",
"name": "car",
"in": "body",
"schema": {
"$ref": "#/definitions/transport.CarUpdateRequest"
}
}
],
"responses": {
"200": {
"description": "Car payload (GET) or update ack (PUT)",
"schema": {
"$ref": "#/definitions/transport.CarWire"
}
},
"204": {
"description": "Delete ack",
"schema": {
"$ref": "#/definitions/transport.CarAck"
}
},
"400": {
"description": "Bad request / invalid input",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"403": {
"description": "System car is immutable",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Car not found",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"409": {
"description": "Car id already exists",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"put": {
"description": "Dispatches on HTTP method. The id is taken from the URL path (` + "`" + `/api/cars/{id}` + "`" + `). System cars are immutable and cannot be updated or deleted.",
"produces": [
"application/json"
],
"tags": [
"cars"
],
"summary": "Get / update / delete a car by id",
"parameters": [
{
"type": "string",
"description": "Car id",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Patch payload (only for PUT)",
"name": "car",
"in": "body",
"schema": {
"$ref": "#/definitions/transport.CarUpdateRequest"
}
}
],
"responses": {
"200": {
"description": "Car payload (GET) or update ack (PUT)",
"schema": {
"$ref": "#/definitions/transport.CarWire"
}
},
"204": {
"description": "Delete ack",
"schema": {
"$ref": "#/definitions/transport.CarAck"
}
},
"400": {
"description": "Bad request / invalid input",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"403": {
"description": "System car is immutable",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Car not found",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"409": {
"description": "Car id already exists",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"delete": {
"description": "Dispatches on HTTP method. The id is taken from the URL path (` + "`" + `/api/cars/{id}` + "`" + `). System cars are immutable and cannot be updated or deleted.",
"produces": [
"application/json"
],
"tags": [
"cars"
],
"summary": "Get / update / delete a car by id",
"parameters": [
{
"type": "string",
"description": "Car id",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Patch payload (only for PUT)",
"name": "car",
"in": "body",
"schema": {
"$ref": "#/definitions/transport.CarUpdateRequest"
}
}
],
"responses": {
"200": {
"description": "Car payload (GET) or update ack (PUT)",
"schema": {
"$ref": "#/definitions/transport.CarWire"
}
},
"204": {
"description": "Delete ack",
"schema": {
"$ref": "#/definitions/transport.CarAck"
}
},
"400": {
"description": "Bad request / invalid input",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"403": {
"description": "System car is immutable",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Car not found",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"409": {
"description": "Car id already exists",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/catalog": {
"get": {
"description": "Returns the full in-memory catalog: all tracks, all cars, and aggregate counters. This is the cheapest way for a UI to bootstrap its initial state.",
"produces": [
"application/json"
],
"tags": [
"catalog"
],
"summary": "Combined catalog snapshot",
"responses": {
"200": {
"description": "Snapshot of tracks, cars and summary counters",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/clans": {
"get": {
"description": "Returns clans ordered by tag. Limit 1..200, default 50.",
"produces": [
"application/json"
],
"tags": [
"clans"
],
"summary": "List clans",
"parameters": [
{
"type": "integer",
"description": "Page size",
"name": "limit",
"in": "query"
},
{
"type": "integer",
"description": "Page offset",
"name": "offset",
"in": "query"
}
],
"responses": {
"200": {
"description": "Page of clans",
"schema": {
"$ref": "#/definitions/transport.ClanListResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"post": {
"description": "Creates a new clan. ` + "`" + `tag` + "`" + ` must be exactly 3 uppercase ASCII letters (e.g. \"ACE\") and unique.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"clans"
],
"summary": "Create a clan",
"parameters": [
{
"description": "Clan to create",
"name": "clan",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/transport.ClanCreateRequest"
}
}
],
"responses": {
"201": {
"description": "Created clan",
"schema": {
"$ref": "#/definitions/transport.Clan"
}
},
"400": {
"description": "Invalid input (tag must be 3 uppercase letters)",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"409": {
"description": "Clan with that tag already exists",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/clans/{id}": {
"get": {
"description": "Dispatches on HTTP method.",
"produces": [
"application/json"
],
"tags": [
"clans"
],
"summary": "Get / update / delete a clan by id",
"parameters": [
{
"type": "string",
"description": "Clan id",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Patch payload (only for PUT)",
"name": "clan",
"in": "body",
"schema": {
"$ref": "#/definitions/transport.ClanUpdateRequest"
}
}
],
"responses": {
"200": {
"description": "Clan payload or update result",
"schema": {
"$ref": "#/definitions/transport.Clan"
}
},
"204": {
"description": "Delete ack",
"schema": {
"$ref": "#/definitions/transport.Clan"
}
},
"400": {
"description": "Bad request",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Clan not found",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"put": {
"description": "Dispatches on HTTP method.",
"produces": [
"application/json"
],
"tags": [
"clans"
],
"summary": "Get / update / delete a clan by id",
"parameters": [
{
"type": "string",
"description": "Clan id",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Patch payload (only for PUT)",
"name": "clan",
"in": "body",
"schema": {
"$ref": "#/definitions/transport.ClanUpdateRequest"
}
}
],
"responses": {
"200": {
"description": "Clan payload or update result",
"schema": {
"$ref": "#/definitions/transport.Clan"
}
},
"204": {
"description": "Delete ack",
"schema": {
"$ref": "#/definitions/transport.Clan"
}
},
"400": {
"description": "Bad request",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Clan not found",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"delete": {
"description": "Dispatches on HTTP method.",
"produces": [
"application/json"
],
"tags": [
"clans"
],
"summary": "Get / update / delete a clan by id",
"parameters": [
{
"type": "string",
"description": "Clan id",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Patch payload (only for PUT)",
"name": "clan",
"in": "body",
"schema": {
"$ref": "#/definitions/transport.ClanUpdateRequest"
}
}
],
"responses": {
"200": {
"description": "Clan payload or update result",
"schema": {
"$ref": "#/definitions/transport.Clan"
}
},
"204": {
"description": "Delete ack",
"schema": {
"$ref": "#/definitions/transport.Clan"
}
},
"400": {
"description": "Bad request",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Clan not found",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/drivers": {
"get": {
"description": "Returns drivers ordered by nickname. Optional ` + "`" + `clan_id` + "`" + ` filter.",
"produces": [
"application/json"
],
"tags": [
"drivers"
],
"summary": "List drivers",
"parameters": [
{
"type": "integer",
"description": "Page size",
"name": "limit",
"in": "query"
},
{
"type": "integer",
"description": "Page offset",
"name": "offset",
"in": "query"
},
{
"type": "string",
"description": "Filter by clan id",
"name": "clan_id",
"in": "query"
}
],
"responses": {
"200": {
"description": "Page of drivers",
"schema": {
"$ref": "#/definitions/transport.DriverListResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"post": {
"description": "Creates a new driver. ` + "`" + `nickname` + "`" + ` must be exactly 3 uppercase ASCII letters (e.g. \"ACE\") and unique. ` + "`" + `clan_id` + "`" + ` is optional and must reference an existing clan.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"drivers"
],
"summary": "Create a driver",
"parameters": [
{
"description": "Driver to create",
"name": "driver",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/transport.DriverCreateRequest"
}
}
],
"responses": {
"201": {
"description": "Created driver",
"schema": {
"$ref": "#/definitions/transport.Driver"
}
},
"400": {
"description": "Invalid input (nickname must be 3 uppercase letters)",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"409": {
"description": "Driver with that nickname already exists",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/drivers/{id}": {
"get": {
"description": "Dispatches on HTTP method. Path ` + "`" + `/api/drivers/by-nick/{nick}` + "`" + ` is a convenience that returns the driver by 3-letter nickname.",
"produces": [
"application/json"
],
"tags": [
"drivers"
],
"summary": "Get / update / delete a driver by id",
"parameters": [
{
"type": "string",
"description": "Driver id",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Patch payload (only for PUT)",
"name": "driver",
"in": "body",
"schema": {
"$ref": "#/definitions/transport.DriverUpdateRequest"
}
}
],
"responses": {
"200": {
"description": "Driver payload or update result",
"schema": {
"$ref": "#/definitions/transport.Driver"
}
},
"204": {
"description": "Delete ack",
"schema": {
"$ref": "#/definitions/transport.Driver"
}
},
"400": {
"description": "Bad request",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Driver not found",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"put": {
"description": "Dispatches on HTTP method. Path ` + "`" + `/api/drivers/by-nick/{nick}` + "`" + ` is a convenience that returns the driver by 3-letter nickname.",
"produces": [
"application/json"
],
"tags": [
"drivers"
],
"summary": "Get / update / delete a driver by id",
"parameters": [
{
"type": "string",
"description": "Driver id",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Patch payload (only for PUT)",
"name": "driver",
"in": "body",
"schema": {
"$ref": "#/definitions/transport.DriverUpdateRequest"
}
}
],
"responses": {
"200": {
"description": "Driver payload or update result",
"schema": {
"$ref": "#/definitions/transport.Driver"
}
},
"204": {
"description": "Delete ack",
"schema": {
"$ref": "#/definitions/transport.Driver"
}
},
"400": {
"description": "Bad request",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Driver not found",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"delete": {
"description": "Dispatches on HTTP method. Path ` + "`" + `/api/drivers/by-nick/{nick}` + "`" + ` is a convenience that returns the driver by 3-letter nickname.",
"produces": [
"application/json"
],
"tags": [
"drivers"
],
"summary": "Get / update / delete a driver by id",
"parameters": [
{
"type": "string",
"description": "Driver id",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Patch payload (only for PUT)",
"name": "driver",
"in": "body",
"schema": {
"$ref": "#/definitions/transport.DriverUpdateRequest"
}
}
],
"responses": {
"200": {
"description": "Driver payload or update result",
"schema": {
"$ref": "#/definitions/transport.Driver"
}
},
"204": {
"description": "Delete ack",
"schema": {
"$ref": "#/definitions/transport.Driver"
}
},
"400": {
"description": "Bad request",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Driver not found",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/drivers/{id}/device": {
"put": {
"description": "Dispatches on HTTP method. Path ` + "`" + `/api/drivers/by-nick/{nick}` + "`" + ` is a convenience that returns the driver by 3-letter nickname.",
"produces": [
"application/json"
],
"tags": [
"drivers"
],
"summary": "Get / update / delete a driver by id",
"parameters": [
{
"type": "string",
"description": "Driver id",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Patch payload (only for PUT)",
"name": "driver",
"in": "body",
"schema": {
"$ref": "#/definitions/transport.DriverUpdateRequest"
}
}
],
"responses": {
"200": {
"description": "Driver payload or update result",
"schema": {
"$ref": "#/definitions/transport.Driver"
}
},
"204": {
"description": "Delete ack",
"schema": {
"$ref": "#/definitions/transport.Driver"
}
},
"400": {
"description": "Bad request",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Driver not found",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/leaderboard": {
"get": {
"description": "Returns paginated leaderboard list. Supports overall leaderboard or track-specific leaderboard for the current year.",
"produces": [
"application/json"
],
"tags": [
"leaderboard"
],
"summary": "Leaderboard",
"parameters": [
{
"type": "string",
"description": "Track ID. If empty, returns overall leaderboard.",
"name": "track_id",
"in": "query"
},
{
"type": "integer",
"description": "Year. Defaults to current year.",
"name": "year",
"in": "query"
},
{
"type": "integer",
"description": "Limit (1..200, default 50)",
"name": "limit",
"in": "query"
},
{
"type": "integer",
"description": "Offset (default 0)",
"name": "offset",
"in": "query"
},
{
"type": "string",
"description": "Current driver ID. Defaults to driver-seed-001 (mock Alice).",
"name": "X-Driver-Id",
"in": "header"
}
],
"responses": {
"200": {
"description": "Leaderboard page and current driver position",
"schema": {
"$ref": "#/definitions/transport.LeaderboardResponse"
}
},
"400": {
"description": "Invalid input",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/leaderboard/tracks": {
"get": {
"description": "Returns the list of scheduled tracks with their status (finished/active/planned), top 3 drivers, and current driver position, sorted by calendar start date.",
"produces": [
"application/json"
],
"tags": [
"leaderboard"
],
"summary": "Track Calendar Leaderboards",
"parameters": [
{
"type": "integer",
"description": "Year. Defaults to current year.",
"name": "year",
"in": "query"
},
{
"type": "string",
"description": "Current driver ID. Defaults to driver-seed-001 (mock Alice).",
"name": "X-Driver-Id",
"in": "header"
}
],
"responses": {
"200": {
"description": "Calendar leaderboards list",
"schema": {
"$ref": "#/definitions/transport.TrackCalendarLeaderboardResponse"
}
},
"400": {
"description": "Invalid input",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/races": {
"get": {
"description": "Returns one keyset page of races. Live races (status lobby|racing) come from in-memory lobby.Service; finished races come from Postgres. The two sources are merged and ordered by (started_ms DESC, id ASC). The cursor is opaque: pass back the next_cursor verbatim from a previous page.\n## Filters\n- ` + "`" + `status` + "`" + ` (comma-separated, e.g. \"racing,finished\"). Recognised values: all, lobby, racing, finished.\n- ` + "`" + `track_id` + "`" + ` (exact match against a physical track id; custom lobbies are not supported in this build).\n- ` + "`" + `limit` + "`" + ` (1..200, default 50)",
"produces": [
"application/json"
],
"tags": [
"races"
],
"summary": "List races (keyset paginated)",
"parameters": [
{
"enum": [
"all",
"lobby",
"racing",
"finished"
],
"type": "string",
"description": "Comma-separated status filter",
"name": "status",
"in": "query"
},
{
"type": "string",
"description": "Filter by physical track id",
"name": "track_id",
"in": "query"
},
{
"type": "string",
"description": "Opaque keyset cursor (next_cursor from previous page)",
"name": "cursor",
"in": "query"
},
{
"type": "integer",
"description": "Page size (1..200, default 50)",
"name": "limit",
"in": "query"
}
],
"responses": {
"200": {
"description": "Page of races",
"schema": {
"$ref": "#/definitions/transport.RaceListResponse"
}
},
"400": {
"description": "Bad request / invalid cursor / invalid status",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/races/plans": {
"get": {
"description": "Returns race plans in ascending start_at order. Pass back the ` + "`" + `next_cursor` + "`" + ` to get the next page.",
"produces": [
"application/json"
],
"tags": [
"plans"
],
"summary": "List race plans (keyset paginated)",
"parameters": [
{
"type": "string",
"description": "Opaque keyset cursor",
"name": "cursor",
"in": "query"
},
{
"type": "integer",
"description": "Page size (default 50)",
"name": "limit",
"in": "query"
}
],
"responses": {
"200": {
"description": "Page of plans",
"schema": {
"$ref": "#/definitions/transport.RacePlanListResponse"
}
},
"400": {
"description": "Invalid cursor",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"post": {
"description": "Creates a recurring or one-off scheduled race. The scheduler materialises a lobby race when ` + "`" + `next_fire_ms` + "`" + ` \u003c= now. If ` + "`" + `interval_s` + "`" + ` \u003e 0 the plan repeats; if ` + "`" + `count` + "`" + ` \u003e 0 the plan disables itself after that many fires.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"plans"
],
"summary": "Create a race plan",
"parameters": [
{
"description": "Plan to create",
"name": "plan",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/transport.RacePlanCreateRequest"
}
}
],
"responses": {
"201": {
"description": "Created plan",
"schema": {
"$ref": "#/definitions/transport.RacePlan"
}
},
"400": {
"description": "Invalid input",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"409": {
"description": "Plan id already exists",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/races/plans/{id}": {
"delete": {
"description": "Removes a plan by id. Materialised races already in the lobby are not affected.",
"produces": [
"application/json"
],
"tags": [
"plans"
],
"summary": "Delete a race plan",
"parameters": [
{
"type": "string",
"description": "Plan id",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "ok",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Plan not found",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/races/queue": {
"get": {
"description": "Returns the queue entries for a driver, oldest first.",
"produces": [
"application/json"
],
"tags": [
"races"
],
"summary": "List driver's queue entries",
"parameters": [
{
"type": "string",
"description": "Driver id",
"name": "X-Driver-Id",
"in": "header"
},
{
"type": "string",
"description": "Driver id (alternative to header)",
"name": "driver_id",
"in": "query"
}
],
"responses": {
"200": {
"description": "Queue entries",
"schema": {
"$ref": "#/definitions/transport.RaceQueueListResponse"
}
},
"400": {
"description": "driver_id required",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"delete": {
"description": "Removes a single (driver, race) entry from the queue. Does not detach the driver from the race if already joined.",
"produces": [
"application/json"
],
"tags": [
"races"
],
"summary": "Leave a queue entry",
"parameters": [
{
"type": "string",
"description": "Driver id",
"name": "X-Driver-Id",
"in": "header"
},
{
"type": "string",
"description": "Driver id",
"name": "driver_id",
"in": "query"
},
{
"type": "string",
"description": "Race id",
"name": "race_id",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "ok",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"400": {
"description": "driver_id and race_id required",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "queue entry not found",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/races/queue/join": {
"post": {
"description": "Puts the driver in the queue for each of the next N (default 3) lobby|countdown races, soonest first. If a race has a free slot, the driver is also attached to it (best effort). Idempotent per (driver, race).",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"races"
],
"summary": "Enqueue driver on next N upcoming races",
"parameters": [
{
"type": "string",
"description": "Driver id (alternative to body)",
"name": "X-Driver-Id",
"in": "header"
},
{
"description": "Driver id and optional limit",
"name": "body",
"in": "body",
"schema": {
"$ref": "#/definitions/transport.RaceQueueJoinRequest"
}
}
],
"responses": {
"200": {
"description": "Queue entries created",
"schema": {
"$ref": "#/definitions/transport.RaceQueueJoinResponse"
}
},
"400": {
"description": "driver_id required",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/races/upcoming": {
"get": {
"description": "Returns up to ` + "`" + `limit` + "`" + ` races currently in status lobby|countdown, ordered soonest first. Useful for the \"join queue\" UI and dashboard.",
"produces": [
"application/json"
],
"tags": [
"races"
],
"summary": "Next N upcoming races",
"parameters": [
{
"type": "integer",
"description": "Number of races to return (1..16, default 3)",
"name": "limit",
"in": "query"
}
],
"responses": {
"200": {
"description": "Upcoming races",
"schema": {
"$ref": "#/definitions/transport.RaceUpcomingResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/tracks": {
"get": {
"description": "Returns all tracks visible to the caller. Optional query filters: ` + "`" + `scope=system|public|private` + "`" + `, ` + "`" + `tag=\u003cstring\u003e` + "`" + `.",
"produces": [
"application/json"
],
"tags": [
"tracks"
],
"summary": "List tracks",
"parameters": [
{
"enum": [
"system",
"public",
"private"
],
"type": "string",
"description": "Filter by visibility",
"name": "scope",
"in": "query"
},
{
"type": "string",
"description": "Filter by tag (case-insensitive)",
"name": "tag",
"in": "query"
}
],
"responses": {
"200": {
"description": "List of tracks with count",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"post": {
"description": "Creates a new track. The server fills ` + "`" + `id` + "`" + ` if the client sends an empty one. ` + "`" + `author_id` + "`" + ` should be the caller's id in production (auth is disabled in PoC).",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"tracks"
],
"summary": "Create a track",
"parameters": [
{
"description": "Track to create",
"name": "track",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/transport.TrackCreateRequest"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/transport.TrackAck"
}
},
"400": {
"description": "Invalid input",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"409": {
"description": "Track id already exists",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/tracks/calendar": {
"get": {
"description": "Returns the full calendar of track active periods.",
"produces": [
"application/json"
],
"tags": [
"tracks"
],
"summary": "Get Track Calendar",
"responses": {
"200": {
"description": "List of scheduled active periods",
"schema": {
"$ref": "#/definitions/transport.TrackCalendarResponse"
}
},
"500": {
"description": "Internal error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"post": {
"description": "Schedules an active period for a physical track.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"tracks"
],
"summary": "Schedule Track Active Period",
"parameters": [
{
"description": "Calendar entry details",
"name": "payload",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/transport.TrackCalendarCreateRequest"
}
}
],
"responses": {
"201": {
"description": "Created calendar entry",
"schema": {
"$ref": "#/definitions/transport.TrackCalendarEntry"
}
},
"400": {
"description": "Invalid input",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Track not found",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/tracks/calendar/{id}": {
"delete": {
"description": "Deletes a scheduled active period by ID.",
"tags": [
"tracks"
],
"summary": "Delete Scheduled Period",
"parameters": [
{
"type": "integer",
"description": "Calendar Entry ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"204": {
"description": "No Content"
},
"404": {
"description": "Not found",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/tracks/{id}": {
"get": {
"description": "Dispatches on HTTP method. The id is taken from the URL path (` + "`" + `/api/tracks/{id}` + "`" + `). System tracks are immutable and cannot be updated or deleted.",
"produces": [
"application/json"
],
"tags": [
"tracks"
],
"summary": "Get / update / delete a track by id",
"parameters": [
{
"type": "string",
"description": "Track id",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Patch payload (only for PUT)",
"name": "track",
"in": "body",
"schema": {
"$ref": "#/definitions/transport.TrackUpdateRequest"
}
}
],
"responses": {
"200": {
"description": "Track payload (GET) or update ack (PUT)",
"schema": {
"$ref": "#/definitions/transport.TrackWire"
}
},
"204": {
"description": "Delete ack",
"schema": {
"$ref": "#/definitions/transport.TrackAck"
}
},
"400": {
"description": "Bad request / invalid input",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"403": {
"description": "System track is immutable",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Track not found",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"409": {
"description": "Track id already exists",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"put": {
"description": "Dispatches on HTTP method. The id is taken from the URL path (` + "`" + `/api/tracks/{id}` + "`" + `). System tracks are immutable and cannot be updated or deleted.",
"produces": [
"application/json"
],
"tags": [
"tracks"
],
"summary": "Get / update / delete a track by id",
"parameters": [
{
"type": "string",
"description": "Track id",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Patch payload (only for PUT)",
"name": "track",
"in": "body",
"schema": {
"$ref": "#/definitions/transport.TrackUpdateRequest"
}
}
],
"responses": {
"200": {
"description": "Track payload (GET) or update ack (PUT)",
"schema": {
"$ref": "#/definitions/transport.TrackWire"
}
},
"204": {
"description": "Delete ack",
"schema": {
"$ref": "#/definitions/transport.TrackAck"
}
},
"400": {
"description": "Bad request / invalid input",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"403": {
"description": "System track is immutable",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Track not found",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"409": {
"description": "Track id already exists",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"delete": {
"description": "Dispatches on HTTP method. The id is taken from the URL path (` + "`" + `/api/tracks/{id}` + "`" + `). System tracks are immutable and cannot be updated or deleted.",
"produces": [
"application/json"
],
"tags": [
"tracks"
],
"summary": "Get / update / delete a track by id",
"parameters": [
{
"type": "string",
"description": "Track id",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Patch payload (only for PUT)",
"name": "track",
"in": "body",
"schema": {
"$ref": "#/definitions/transport.TrackUpdateRequest"
}
}
],
"responses": {
"200": {
"description": "Track payload (GET) or update ack (PUT)",
"schema": {
"$ref": "#/definitions/transport.TrackWire"
}
},
"204": {
"description": "Delete ack",
"schema": {
"$ref": "#/definitions/transport.TrackAck"
}
},
"400": {
"description": "Bad request / invalid input",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"403": {
"description": "System track is immutable",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"404": {
"description": "Track not found",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"409": {
"description": "Track id already exists",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"500": {
"description": "Internal server error",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/api/version": {
"get": {
"description": "Returns the current server version.",
"produces": [
"application/json"
],
"tags": [
"system"
],
"summary": "Get server version",
"responses": {
"200": {
"description": "Success",
"schema": {
"$ref": "#/definitions/transport.VersionResponse"
}
}
}
}
},
"/api/video/control": {
"post": {
"description": "Sends a text command or a JSON control packet to the ESP32 command port (UDP 8888).",
"produces": [
"application/json"
],
"tags": [
"video"
],
"summary": "Send command to ESP32",
"parameters": [
{
"type": "integer",
"description": "Device ID (0..127)",
"name": "id",
"in": "query"
},
{
"type": "string",
"description": "Command text (legacy)",
"name": "cmd",
"in": "query"
},
{
"description": "Control packet JSON (steer, throttle, brake, gear, flags)",
"name": "body",
"in": "body",
"schema": {
"type": "object",
"additionalProperties": {
"type": "integer"
}
}
}
],
"responses": {
"200": {
"description": "Command sent",
"schema": {
"type": "object",
"additionalProperties": true
}
},
"400": {
"description": "Missing parameter or invalid body",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal error",
"schema": {
"type": "string"
}
}
}
}
},
"/api/video/stream": {
"get": {
"description": "Streams video frames received via UDP as multipart/x-mixed-replace. Suitable for raw img tags in browser.",
"produces": [
"multipart/x-mixed-replace"
],
"tags": [
"video"
],
"summary": "Get MJPEG video stream from ESP32",
"parameters": [
{
"type": "integer",
"description": "Device ID (0..127)",
"name": "id",
"in": "query"
}
],
"responses": {
"200": {
"description": "MJPEG stream",
"schema": {
"type": "string"
}
}
}
}
},
"/api/webrtc/connect": {
"post": {
"description": "Accepts SDP Offer, negotiates peer connection, and returns SDP Answer. Routes video and control data via DataChannels.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"webrtc"
],
"summary": "Establish WebRTC connection for driver-car 1-to-1 proxying",
"parameters": [
{
"description": "SDP Offer details",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/transport.WebRTCConnectRequest"
}
}
],
"responses": {
"200": {
"description": "SDP Answer negotiation success",
"schema": {
"$ref": "#/definitions/transport.WebRTCConnectResponse"
}
}
}
}
},
"/health": {
"get": {
"description": "Returns 200 OK with current engine phase, WS connection count and total snapshot drops. Intended for load-balancer health checks.",
"produces": [
"application/json"
],
"tags": [
"system"
],
"summary": "Liveness probe",
"responses": {
"200": {
"description": "Server is up",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/stats": {
"get": {
"description": "Returns the current race phase plus realtime counters: active WS connections and total snapshots dropped due to slow consumers.",
"produces": [
"application/json"
],
"tags": [
"system"
],
"summary": "Realtime runtime stats",
"responses": {
"200": {
"description": "Runtime counters",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/ws": {
"get": {
"description": "Opens a bidirectional WebSocket for the realtime race protocol. The server replies with ` + "`" + `server_hello` + "`" + `, then broadcasts ` + "`" + `race_snapshot` + "`" + ` frames at ` + "`" + `X0GP_SNAPSHOT_HZ` + "`" + `.\n## Frame format\nEvery frame is a JSON envelope of the form:\n` + "`" + `` + "`" + `` + "`" + `json\n{ \"type\": \"\u003cmessage_type\u003e\", \"seq\": 42, \"ts_ms\": 1700000000000, \"payload\": { ... } }\n` + "`" + `` + "`" + `` + "`" + `\n` + "`" + `type` + "`" + ` discriminates the payload; ` + "`" + `payload` + "`" + ` is documented per message type in the schemas listed in ` + "`" + `components/schemas` + "`" + ` (see ` + "`" + `Envelope` + "`" + `, ` + "`" + `ClientHello` + "`" + `, ` + "`" + `ServerHello` + "`" + `, ` + "`" + `InputState` + "`" + `, ` + "`" + `JoinRace` + "`" + `, ` + "`" + `LeaveRace` + "`" + `, ` + "`" + `RaceSnapshot` + "`" + `, ` + "`" + `InputAck` + "`" + `, ` + "`" + `RaceEvent` + "`" + `, ` + "`" + `Ping` + "`" + `, ` + "`" + `Pong` + "`" + `, ` + "`" + `ErrorMsg` + "`" + `, plus the catalog/lobby/stats envelopes).\n## Client -\u003e Server\n- ` + "`" + `client_hello` + "`" + ` — first frame after connect. Sent once.\n- ` + "`" + `join_race` + "`" + ` / ` + "`" + `leave_race` + "`" + ` — join or leave the race session.\n- ` + "`" + `input_state` + "`" + ` — high-rate control (steering/throttle/brake/gear). Send at 60-120 Hz.\n- ` + "`" + `ping` + "`" + ` — round-trip time probe.\n- ` + "`" + `track_*` + "`" + ` / ` + "`" + `car_*` + "`" + ` — catalog CRUD (mirror of the REST /api/* endpoints).\n- ` + "`" + `lobby_*` + "`" + ` — lobby management.\n- ` + "`" + `stats_request` + "`" + ` — ask for a stats snapshot.\n## Server -\u003e Client\n- ` + "`" + `server_hello` + "`" + ` — handshake reply; includes session id and engine config.\n- ` + "`" + `race_snapshot` + "`" + ` — periodic authoritative state broadcast.\n- ` + "`" + `input_ack` + "`" + ` — per-input acknowledgement (optional; drop-tolerant).\n- ` + "`" + `race_event` + "`" + ` — start/lap/sector/dnf/finish.\n- ` + "`" + `track_snapshot` + "`" + ` / ` + "`" + `car_snapshot` + "`" + ` / ` + "`" + `catalog_summary` + "`" + ` — catalog feeds on change.\n- ` + "`" + `lobby_snapshot` + "`" + ` — full lobby state on change.\n- ` + "`" + `stats_snapshot` + "`" + ` / ` + "`" + `race_standings` + "`" + ` / ` + "`" + `lap_recorded` + "`" + ` / ` + "`" + `race_result` + "`" + ` — stats feeds.\n- ` + "`" + `error` + "`" + ` — protocol or validation error.\n## Notes\n- Origin checks are disabled in PoC mode (` + "`" + `CheckOrigin: always-allow` + "`" + `).\n- Ping interval: 20 s. Pong wait: 60 s. Max frame size: 8 KiB.\n- Input acks are best-effort and silently dropped under load.",
"tags": [
"websocket"
],
"summary": "WebSocket endpoint (binary JSON envelope protocol)",
"responses": {
"101": {
"description": "Switching protocols. Subsequent frames follow the envelope schema.",
"schema": {
"$ref": "#/definitions/transport.Envelope"
}
}
}
}
}
},
"definitions": {
"transport.BatteryWire": {
"type": "object",
"properties": {
"capacity_mah": {
"type": "integer"
},
"cells": {
"type": "integer"
},
"chemistry": {
"type": "string"
},
"voltage_v": {
"type": "number"
}
}
},
"transport.CarAck": {
"type": "object",
"properties": {
"car": {
"$ref": "#/definitions/transport.CarWire"
},
"error": {
"type": "string"
},
"id": {
"type": "string"
},
"ok": {
"type": "boolean"
}
}
},
"transport.CarCreateRequest": {
"type": "object",
"properties": {
"car": {
"$ref": "#/definitions/transport.CarWire"
}
}
},
"transport.CarPatch": {
"type": "object",
"properties": {
"active": {
"type": "boolean"
},
"avatar_url": {
"type": "string"
},
"battery": {
"$ref": "#/definitions/transport.BatteryWire"
},
"chassis": {
"$ref": "#/definitions/transport.ChassisWire"
},
"color_hex": {
"type": "string"
},
"drive": {
"type": "string"
},
"height_mm": {
"type": "number"
},
"length_mm": {
"type": "number"
},
"motor": {
"$ref": "#/definitions/transport.MotorWire"
},
"name": {
"type": "string"
},
"scale": {
"type": "integer"
},
"top_speed_ms": {
"type": "number"
},
"visibility": {
"type": "string"
},
"weight_g": {
"type": "number"
},
"width_mm": {
"type": "number"
}
}
},
"transport.CarUpdateRequest": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"patch": {
"$ref": "#/definitions/transport.CarPatch"
}
}
},
"transport.CarWire": {
"type": "object",
"properties": {
"active": {
"type": "boolean"
},
"avatar_url": {
"type": "string"
},
"battery": {
"$ref": "#/definitions/transport.BatteryWire"
},
"best_lap_ms": {
"type": "integer"
},
"chassis": {
"$ref": "#/definitions/transport.ChassisWire"
},
"color_hex": {
"type": "string"
},
"created_ms": {
"type": "integer"
},
"drive": {
"type": "string"
},
"height_mm": {
"type": "number"
},
"id": {
"type": "string"
},
"length_mm": {
"type": "number"
},
"motor": {
"$ref": "#/definitions/transport.MotorWire"
},
"name": {
"type": "string"
},
"owner_id": {
"type": "string"
},
"scale": {
"type": "integer"
},
"top_speed_ms": {
"type": "number"
},
"total_distance_m": {
"type": "number"
},
"total_laps": {
"type": "integer"
},
"total_races": {
"type": "integer"
},
"updated_ms": {
"type": "integer"
},
"visibility": {
"type": "string"
},
"weight_g": {
"type": "number"
},
"width_mm": {
"type": "number"
}
}
},
"transport.ChassisWire": {
"type": "object",
"properties": {
"material": {
"type": "string"
},
"model": {
"type": "string"
},
"printed": {
"type": "boolean"
},
"track_mm": {
"type": "number"
},
"wheelbase_mm": {
"type": "number"
}
}
},
"transport.Clan": {
"type": "object",
"properties": {
"avatar_url": {
"type": "string",
"example": "https://cdn.example.com/c/ace.png"
},
"created_ms": {
"type": "integer"
},
"id": {
"type": "string",
"example": "clan-1782000-1"
},
"name": {
"type": "string",
"example": "Ace Racing"
},
"tag": {
"type": "string",
"example": "ACE"
},
"updated_ms": {
"type": "integer"
}
}
},
"transport.ClanCreateRequest": {
"type": "object",
"properties": {
"avatar_url": {
"type": "string",
"example": "https://cdn.example.com/c/ace.png"
},
"id": {
"type": "string",
"example": "clan-1782000-1"
},
"name": {
"type": "string",
"example": "Ace Racing"
},
"tag": {
"type": "string",
"example": "ACE"
}
}
},
"transport.ClanListResponse": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/transport.Clan"
}
}
}
},
"transport.ClanUpdateRequest": {
"type": "object",
"properties": {
"avatar_url": {
"type": "string",
"example": "https://cdn.example.com/c/ace.png"
},
"name": {
"type": "string",
"example": "Ace Racing"
}
}
},
"transport.Driver": {
"type": "object",
"properties": {
"avatar_url": {
"type": "string",
"example": "https://cdn.example.com/u/ace.png"
},
"clan_id": {
"type": "string",
"example": "clan-1782000-1"
},
"created_ms": {
"type": "integer"
},
"id": {
"type": "string",
"example": "driver-1782000-123"
},
"name": {
"type": "string",
"example": "Alice"
},
"nickname": {
"type": "string",
"example": "ACE"
},
"updated_ms": {
"type": "integer"
}
}
},
"transport.DriverCreateRequest": {
"type": "object",
"properties": {
"avatar_url": {
"type": "string",
"example": "https://cdn.example.com/u/ace.png"
},
"clan_id": {
"type": "string",
"example": "clan-1782000-1"
},
"id": {
"type": "string",
"example": "driver-1782000-123"
},
"name": {
"type": "string",
"example": "Alice"
},
"nickname": {
"type": "string",
"example": "ACE"
}
}
},
"transport.DriverListResponse": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/transport.Driver"
}
}
}
},
"transport.DriverUpdateRequest": {
"type": "object",
"properties": {
"avatar_url": {
"type": "string",
"example": "https://cdn.example.com/u/ace.png"
},
"clan_id": {
"type": "string",
"example": "clan-1782000-1"
},
"name": {
"type": "string",
"example": "Alice"
}
}
},
"transport.Envelope": {
"type": "object",
"properties": {
"payload": {},
"seq": {
"type": "integer"
},
"ts_ms": {
"type": "integer"
},
"type": {
"$ref": "#/definitions/transport.MessageType"
}
}
},
"transport.LeaderboardEntry": {
"type": "object",
"properties": {
"avatar_url": {
"type": "string",
"example": "https://cdn.example.com/u/ace.png"
},
"best_pos": {
"description": "Only populated for track-specific leaderboard",
"type": "integer",
"example": 1
},
"best_time_ms": {
"type": "integer",
"example": 15000
},
"clan_id": {
"type": "string",
"example": "clan-seed-001"
},
"clan_tag": {
"type": "string",
"example": "ACE"
},
"driver_id": {
"type": "string",
"example": "driver-seed-001"
},
"name": {
"type": "string",
"example": "Alice"
},
"nickname": {
"type": "string",
"example": "ACE"
},
"points": {
"type": "integer",
"example": 25
},
"rank": {
"type": "integer",
"example": 1
}
}
},
"transport.LeaderboardResponse": {
"type": "object",
"properties": {
"current_driver": {
"$ref": "#/definitions/transport.LeaderboardEntry"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/transport.LeaderboardEntry"
}
},
"limit": {
"type": "integer",
"example": 50
},
"offset": {
"type": "integer",
"example": 0
},
"total": {
"type": "integer",
"example": 8
}
}
},
"transport.LobbyRace": {
"type": "object",
"properties": {
"created_ms": {
"type": "integer"
},
"driver_ids": {
"type": "array",
"items": {
"type": "string"
}
},
"id": {
"type": "string"
},
"laps": {
"type": "integer"
},
"max_cars": {
"type": "integer"
},
"name": {
"type": "string"
},
"podium": {
"description": "Podium is populated only for finished races. Empty for live ones.",
"type": "array",
"items": {
"$ref": "#/definitions/transport.RacePodiumEntry"
}
},
"started_ms": {
"type": "integer"
},
"status": {
"type": "string"
},
"time_limit_s": {
"type": "integer"
},
"track_id": {
"type": "string"
}
}
},
"transport.MessageType": {
"type": "string",
"enum": [
"client_hello",
"join_race",
"leave_race",
"input_state",
"chat_message",
"ping",
"lobby_list",
"lobby_create",
"lobby_quick_play",
"lobby_join_race",
"lobby_leave_race",
"lobby_kick_driver",
"lobby_delete",
"track_list",
"track_get",
"track_create",
"track_update",
"track_delete",
"car_list",
"car_get",
"car_create",
"car_update",
"car_delete",
"stats_request",
"server_hello",
"race_snapshot",
"input_ack",
"correction",
"race_event",
"pong",
"error",
"lobby_snapshot",
"track_snapshot",
"track_ack",
"car_snapshot",
"car_ack",
"catalog_summary",
"stats_snapshot",
"driver_profile",
"lap_recorded",
"race_result",
"race_standings"
],
"x-enum-varnames": [
"TypeClientHello",
"TypeJoinRace",
"TypeLeaveRace",
"TypeInputState",
"TypeChatMessage",
"TypePing",
"TypeLobbyList",
"TypeLobbyCreate",
"TypeLobbyQuickPlay",
"TypeLobbyJoinRace",
"TypeLobbyLeaveRace",
"TypeLobbyKickDriver",
"TypeLobbyDelete",
"TypeTrackList",
"TypeTrackGet",
"TypeTrackCreate",
"TypeTrackUpdate",
"TypeTrackDelete",
"TypeCarList",
"TypeCarGet",
"TypeCarCreate",
"TypeCarUpdate",
"TypeCarDelete",
"TypeStatsRequest",
"TypeServerHello",
"TypeRaceSnapshot",
"TypeInputAck",
"TypeCorrection",
"TypeRaceEvent",
"TypePong",
"TypeError",
"TypeLobbySnapshot",
"TypeTrackSnapshot",
"TypeTrackAck",
"TypeCarSnapshot",
"TypeCarAck",
"TypeCatalogSummary",
"TypeStatsSnapshot",
"TypeDriverProfile",
"TypeLapRecorded",
"TypeRaceResult",
"TypeRaceStandings"
]
},
"transport.MotorWire": {
"type": "object",
"properties": {
"class": {
"type": "string"
},
"kind": {
"type": "string"
},
"kv": {
"type": "integer"
},
"power_w": {
"type": "number"
}
}
},
"transport.RaceListResponse": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/transport.LobbyRace"
}
},
"next_cursor": {
"type": "string",
"example": "MTcwMDAwMDAwMDAwMDpyYWNlLWFiYzpmaW5pc2hlZA"
}
}
},
"transport.RacePlan": {
"type": "object",
"properties": {
"count": {
"type": "integer",
"example": 0
},
"created_ms": {
"type": "integer"
},
"enabled": {
"type": "boolean",
"example": true
},
"fires_done": {
"type": "integer"
},
"id": {
"type": "string",
"example": "plan-1730000-1"
},
"interval_s": {
"type": "integer",
"example": 3600
},
"laps": {
"type": "integer",
"example": 10
},
"max_cars": {
"type": "integer",
"example": 4
},
"name": {
"type": "string",
"example": "Daily Monza"
},
"next_fire_ms": {
"type": "integer"
},
"start_at_ms": {
"type": "integer",
"example": 1730000000000
},
"time_limit_s": {
"type": "integer"
},
"track_id": {
"type": "string",
"example": "default"
},
"updated_ms": {
"type": "integer"
}
}
},
"transport.RacePlanCreateRequest": {
"type": "object",
"properties": {
"count": {
"type": "integer",
"example": 0
},
"enabled": {
"type": "boolean",
"example": true
},
"id": {
"type": "string",
"example": "plan-1730000-1"
},
"interval_s": {
"type": "integer",
"example": 3600
},
"laps": {
"type": "integer",
"example": 10
},
"max_cars": {
"type": "integer",
"example": 4
},
"name": {
"type": "string",
"example": "Daily Monza"
},
"start_at_ms": {
"type": "integer",
"example": 1730000000000
},
"time_limit_s": {
"type": "integer"
},
"track_id": {
"type": "string",
"example": "default"
}
}
},
"transport.RacePlanListResponse": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/transport.RacePlan"
}
},
"next_cursor": {
"type": "string"
}
}
},
"transport.RacePodiumEntry": {
"type": "object",
"properties": {
"avatar_url": {
"type": "string"
},
"clan_id": {
"type": "string",
"example": "clan-bob"
},
"driver_id": {
"type": "string",
"example": "driver-alice"
},
"name": {
"type": "string",
"example": "driver-alice"
},
"nickname": {
"type": "string",
"example": "driver-alice"
},
"position": {
"type": "integer",
"example": 1
},
"total_time_ms": {
"type": "integer",
"example": 123456
}
}
},
"transport.RaceQueueEntry": {
"type": "object",
"properties": {
"driver_id": {
"type": "string",
"example": "driver-42"
},
"enqueued_ms": {
"type": "integer",
"example": 1730000000000
},
"plan_id": {
"type": "string",
"example": "plan-1730000-1"
},
"race_id": {
"type": "string",
"example": "race-1730000-7"
}
}
},
"transport.RaceQueueJoinRequest": {
"type": "object",
"properties": {
"driver_id": {
"type": "string",
"example": "driver-42"
},
"limit": {
"type": "integer",
"example": 3
}
}
},
"transport.RaceQueueJoinResponse": {
"type": "object",
"properties": {
"joined": {
"type": "array",
"items": {
"$ref": "#/definitions/transport.RaceQueueEntry"
}
},
"skipped": {
"type": "array",
"items": {
"type": "string"
},
"example": [
"race-1",
"race-2"
]
}
}
},
"transport.RaceQueueListResponse": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/transport.RaceQueueEntry"
}
}
}
},
"transport.RaceUpcomingItem": {
"type": "object",
"properties": {
"meta": {
"$ref": "#/definitions/transport.LobbyRace"
},
"plan_id": {
"type": "string"
},
"queue_len": {
"type": "integer"
},
"start_at_ms": {
"type": "integer"
}
}
},
"transport.RaceUpcomingResponse": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/transport.RaceUpcomingItem"
}
}
}
},
"transport.TrackAck": {
"type": "object",
"properties": {
"error": {
"type": "string"
},
"id": {
"type": "string"
},
"ok": {
"type": "boolean"
},
"track": {
"$ref": "#/definitions/transport.TrackWire"
}
}
},
"transport.TrackBounds": {
"type": "object",
"properties": {
"length_m": {
"type": "number"
},
"max_x": {
"type": "number"
},
"max_y": {
"type": "number"
},
"min_x": {
"type": "number"
},
"min_y": {
"type": "number"
},
"width_m": {
"type": "number"
}
}
},
"transport.TrackCalendarCreateRequest": {
"type": "object",
"properties": {
"end_at": {
"type": "string",
"example": "2026-07-30T00:00:00Z"
},
"start_at": {
"type": "string",
"example": "2026-07-15T00:00:00Z"
},
"track_id": {
"type": "string",
"example": "monaco"
}
}
},
"transport.TrackCalendarEntry": {
"type": "object",
"properties": {
"end_at": {
"type": "string",
"example": "2026-07-30T00:00:00Z"
},
"id": {
"type": "integer",
"example": 1
},
"start_at": {
"type": "string",
"example": "2026-07-15T00:00:00Z"
},
"track_id": {
"type": "string",
"example": "monaco"
}
}
},
"transport.TrackCalendarLeaderboard": {
"type": "object",
"properties": {
"current_driver": {
"$ref": "#/definitions/transport.LeaderboardEntry"
},
"end_at": {
"type": "string",
"example": "2026-07-30T00:00:00Z"
},
"podium": {
"type": "array",
"items": {
"$ref": "#/definitions/transport.LeaderboardEntry"
}
},
"start_at": {
"type": "string",
"example": "2026-07-15T00:00:00Z"
},
"status": {
"type": "string",
"example": "active"
},
"track_id": {
"type": "string",
"example": "monaco"
},
"track_name": {
"type": "string",
"example": "Monaco"
}
}
},
"transport.TrackCalendarLeaderboardResponse": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/transport.TrackCalendarLeaderboard"
}
}
}
},
"transport.TrackCalendarResponse": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/transport.TrackCalendarEntry"
}
}
}
},
"transport.TrackCreateRequest": {
"type": "object",
"properties": {
"track": {
"$ref": "#/definitions/transport.TrackWire"
}
}
},
"transport.TrackPatch": {
"type": "object",
"properties": {
"centerline": {
"type": "array",
"items": {
"$ref": "#/definitions/transport.TrackWaypoint"
}
},
"description": {
"type": "string"
},
"lane_width_m": {
"type": "number"
},
"length_m": {
"type": "number"
},
"name": {
"type": "string"
},
"scale": {
"type": "integer"
},
"surface": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"visibility": {
"type": "string"
},
"width_m": {
"type": "number"
}
}
},
"transport.TrackUpdateRequest": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"patch": {
"$ref": "#/definitions/transport.TrackPatch"
}
}
},
"transport.TrackWaypoint": {
"type": "object",
"properties": {
"curvature": {
"type": "number"
},
"heading_rad": {
"type": "number"
},
"speed_ms": {
"type": "number"
},
"x": {
"type": "number"
},
"y": {
"type": "number"
}
}
},
"transport.TrackWire": {
"type": "object",
"properties": {
"author_id": {
"type": "string"
},
"best_lap_holder": {
"type": "string"
},
"best_lap_ms": {
"type": "integer"
},
"bounds": {
"$ref": "#/definitions/transport.TrackBounds"
},
"centerline": {
"type": "array",
"items": {
"$ref": "#/definitions/transport.TrackWaypoint"
}
},
"created_ms": {
"type": "integer"
},
"description": {
"type": "string"
},
"id": {
"type": "string"
},
"is_active": {
"type": "boolean",
"example": true
},
"lane_width_m": {
"type": "number"
},
"length_m": {
"type": "number"
},
"name": {
"type": "string"
},
"scale": {
"type": "integer"
},
"surface": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"updated_ms": {
"type": "integer"
},
"visibility": {
"type": "string"
},
"width_m": {
"type": "number"
}
}
},
"transport.VersionResponse": {
"type": "object",
"properties": {
"version": {
"type": "string",
"example": "0.1.0-poc"
}
}
},
"transport.WebRTCConnectRequest": {
"type": "object",
"properties": {
"driver_id": {
"type": "string",
"example": "driver-alice"
},
"sdp": {
"type": "string"
},
"type": {
"type": "string",
"example": "offer"
}
}
},
"transport.WebRTCConnectResponse": {
"type": "object",
"properties": {
"sdp": {
"type": "string"
},
"type": {
"type": "string",
"example": "answer"
}
}
}
}
}`
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{
Version: "0.1.0-poc",
Host: "localhost:8080",
BasePath: "/",
Schemes: []string{"http", "ws"},
Title: "x0gp PoC server API",
Description: "Minimal HTTP+WebSocket server for the x0gp RC racing PoC. REST endpoints under /api/* manage the track/car catalog; /health and /stats expose server liveness and realtime metrics; /ws carries the realtime race protocol.\nAuth is intentionally disabled in PoC mode (X0GP_DEV_MODE=true). All catalog mutations are allowed without a JWT.",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}
func init() {
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}