Files
x0gp/server/docs/swagger.yaml
T

2369 lines
62 KiB
YAML

basePath: /
definitions:
transport.BatteryWire:
properties:
capacity_mah:
type: integer
cells:
type: integer
chemistry:
type: string
voltage_v:
type: number
type: object
transport.CarAck:
properties:
car:
$ref: '#/definitions/transport.CarWire'
error:
type: string
id:
type: string
ok:
type: boolean
type: object
transport.CarCreateRequest:
properties:
car:
$ref: '#/definitions/transport.CarWire'
type: object
transport.CarPatch:
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
type: object
transport.CarUpdateRequest:
properties:
id:
type: string
patch:
$ref: '#/definitions/transport.CarPatch'
type: object
transport.CarWire:
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
type: object
transport.ChassisWire:
properties:
material:
type: string
model:
type: string
printed:
type: boolean
track_mm:
type: number
wheelbase_mm:
type: number
type: object
transport.Clan:
properties:
avatar_url:
example: https://cdn.example.com/c/ace.png
type: string
created_ms:
type: integer
id:
example: clan-1782000-1
type: string
name:
example: Ace Racing
type: string
tag:
example: ACE
type: string
updated_ms:
type: integer
type: object
transport.ClanCreateRequest:
properties:
avatar_url:
example: https://cdn.example.com/c/ace.png
type: string
id:
example: clan-1782000-1
type: string
name:
example: Ace Racing
type: string
tag:
example: ACE
type: string
type: object
transport.ClanListResponse:
properties:
count:
type: integer
items:
items:
$ref: '#/definitions/transport.Clan'
type: array
type: object
transport.ClanUpdateRequest:
properties:
avatar_url:
example: https://cdn.example.com/c/ace.png
type: string
name:
example: Ace Racing
type: string
type: object
transport.Driver:
properties:
avatar_url:
example: https://cdn.example.com/u/ace.png
type: string
clan_id:
example: clan-1782000-1
type: string
created_ms:
type: integer
id:
example: driver-1782000-123
type: string
name:
example: Alice
type: string
nickname:
example: ACE
type: string
updated_ms:
type: integer
type: object
transport.DriverCreateRequest:
properties:
avatar_url:
example: https://cdn.example.com/u/ace.png
type: string
clan_id:
example: clan-1782000-1
type: string
id:
example: driver-1782000-123
type: string
name:
example: Alice
type: string
nickname:
example: ACE
type: string
type: object
transport.DriverListResponse:
properties:
count:
type: integer
items:
items:
$ref: '#/definitions/transport.Driver'
type: array
type: object
transport.DriverUpdateRequest:
properties:
avatar_url:
example: https://cdn.example.com/u/ace.png
type: string
clan_id:
example: clan-1782000-1
type: string
name:
example: Alice
type: string
type: object
transport.Envelope:
properties:
payload: {}
seq:
type: integer
ts_ms:
type: integer
type:
$ref: '#/definitions/transport.MessageType'
type: object
transport.LeaderboardEntry:
properties:
avatar_url:
example: https://cdn.example.com/u/ace.png
type: string
best_pos:
description: Only populated for track-specific leaderboard
example: 1
type: integer
best_time_ms:
example: 15000
type: integer
clan_id:
example: clan-seed-001
type: string
clan_tag:
example: ACE
type: string
driver_id:
example: driver-seed-001
type: string
name:
example: Alice
type: string
nickname:
example: ACE
type: string
points:
example: 25
type: integer
rank:
example: 1
type: integer
type: object
transport.LeaderboardResponse:
properties:
current_driver:
$ref: '#/definitions/transport.LeaderboardEntry'
items:
items:
$ref: '#/definitions/transport.LeaderboardEntry'
type: array
limit:
example: 50
type: integer
offset:
example: 0
type: integer
total:
example: 8
type: integer
type: object
transport.LobbyRace:
properties:
created_ms:
type: integer
driver_ids:
items:
type: string
type: array
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.
items:
$ref: '#/definitions/transport.RacePodiumEntry'
type: array
started_ms:
type: integer
status:
type: string
time_limit_s:
type: integer
track_id:
type: string
type: object
transport.MessageType:
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
type: string
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:
properties:
class:
type: string
kind:
type: string
kv:
type: integer
power_w:
type: number
type: object
transport.RaceListResponse:
properties:
count:
type: integer
items:
items:
$ref: '#/definitions/transport.LobbyRace'
type: array
next_cursor:
example: MTcwMDAwMDAwMDAwMDpyYWNlLWFiYzpmaW5pc2hlZA
type: string
type: object
transport.RacePlan:
properties:
count:
example: 0
type: integer
created_ms:
type: integer
enabled:
example: true
type: boolean
fires_done:
type: integer
id:
example: plan-1730000-1
type: string
interval_s:
example: 3600
type: integer
laps:
example: 10
type: integer
max_cars:
example: 4
type: integer
name:
example: Daily Monza
type: string
next_fire_ms:
type: integer
start_at_ms:
example: 1730000000000
type: integer
time_limit_s:
type: integer
track_id:
example: default
type: string
updated_ms:
type: integer
type: object
transport.RacePlanCreateRequest:
properties:
count:
example: 0
type: integer
enabled:
example: true
type: boolean
id:
example: plan-1730000-1
type: string
interval_s:
example: 3600
type: integer
laps:
example: 10
type: integer
max_cars:
example: 4
type: integer
name:
example: Daily Monza
type: string
start_at_ms:
example: 1730000000000
type: integer
time_limit_s:
type: integer
track_id:
example: default
type: string
type: object
transport.RacePlanListResponse:
properties:
count:
type: integer
items:
items:
$ref: '#/definitions/transport.RacePlan'
type: array
next_cursor:
type: string
type: object
transport.RacePodiumEntry:
properties:
avatar_url:
type: string
clan_id:
example: clan-bob
type: string
driver_id:
example: driver-alice
type: string
name:
example: driver-alice
type: string
nickname:
example: driver-alice
type: string
position:
example: 1
type: integer
total_time_ms:
example: 123456
type: integer
type: object
transport.RaceQueueEntry:
properties:
driver_id:
example: driver-42
type: string
enqueued_ms:
example: 1730000000000
type: integer
plan_id:
example: plan-1730000-1
type: string
race_id:
example: race-1730000-7
type: string
type: object
transport.RaceQueueJoinRequest:
properties:
driver_id:
example: driver-42
type: string
limit:
example: 3
type: integer
type: object
transport.RaceQueueJoinResponse:
properties:
joined:
items:
$ref: '#/definitions/transport.RaceQueueEntry'
type: array
skipped:
example:
- race-1
- race-2
items:
type: string
type: array
type: object
transport.RaceQueueListResponse:
properties:
count:
type: integer
items:
items:
$ref: '#/definitions/transport.RaceQueueEntry'
type: array
type: object
transport.RaceUpcomingItem:
properties:
meta:
$ref: '#/definitions/transport.LobbyRace'
plan_id:
type: string
queue_len:
type: integer
start_at_ms:
type: integer
type: object
transport.RaceUpcomingResponse:
properties:
count:
type: integer
items:
items:
$ref: '#/definitions/transport.RaceUpcomingItem'
type: array
type: object
transport.TrackAck:
properties:
error:
type: string
id:
type: string
ok:
type: boolean
track:
$ref: '#/definitions/transport.TrackWire'
type: object
transport.TrackBounds:
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
type: object
transport.TrackCalendarCreateRequest:
properties:
end_at:
example: "2026-07-30T00:00:00Z"
type: string
start_at:
example: "2026-07-15T00:00:00Z"
type: string
track_id:
example: monaco
type: string
type: object
transport.TrackCalendarEntry:
properties:
end_at:
example: "2026-07-30T00:00:00Z"
type: string
id:
example: 1
type: integer
start_at:
example: "2026-07-15T00:00:00Z"
type: string
track_id:
example: monaco
type: string
type: object
transport.TrackCalendarLeaderboard:
properties:
current_driver:
$ref: '#/definitions/transport.LeaderboardEntry'
end_at:
example: "2026-07-30T00:00:00Z"
type: string
podium:
items:
$ref: '#/definitions/transport.LeaderboardEntry'
type: array
start_at:
example: "2026-07-15T00:00:00Z"
type: string
status:
example: active
type: string
track_id:
example: monaco
type: string
track_name:
example: Monaco
type: string
type: object
transport.TrackCalendarLeaderboardResponse:
properties:
items:
items:
$ref: '#/definitions/transport.TrackCalendarLeaderboard'
type: array
type: object
transport.TrackCalendarResponse:
properties:
items:
items:
$ref: '#/definitions/transport.TrackCalendarEntry'
type: array
type: object
transport.TrackCreateRequest:
properties:
track:
$ref: '#/definitions/transport.TrackWire'
type: object
transport.TrackPatch:
properties:
centerline:
items:
$ref: '#/definitions/transport.TrackWaypoint'
type: array
description:
type: string
lane_width_m:
type: number
length_m:
type: number
name:
type: string
scale:
type: integer
surface:
type: string
tags:
items:
type: string
type: array
visibility:
type: string
width_m:
type: number
type: object
transport.TrackUpdateRequest:
properties:
id:
type: string
patch:
$ref: '#/definitions/transport.TrackPatch'
type: object
transport.TrackWaypoint:
properties:
curvature:
type: number
heading_rad:
type: number
speed_ms:
type: number
x:
type: number
"y":
type: number
type: object
transport.TrackWire:
properties:
author_id:
type: string
best_lap_holder:
type: string
best_lap_ms:
type: integer
bounds:
$ref: '#/definitions/transport.TrackBounds'
centerline:
items:
$ref: '#/definitions/transport.TrackWaypoint'
type: array
created_ms:
type: integer
description:
type: string
id:
type: string
is_active:
example: true
type: boolean
lane_width_m:
type: number
length_m:
type: number
name:
type: string
scale:
type: integer
surface:
type: string
tags:
items:
type: string
type: array
updated_ms:
type: integer
visibility:
type: string
width_m:
type: number
type: object
transport.VersionResponse:
properties:
version:
example: 0.1.0-poc
type: string
type: object
transport.WebRTCConnectRequest:
properties:
driver_id:
example: driver-alice
type: string
sdp:
type: string
type:
example: offer
type: string
type: object
transport.WebRTCConnectResponse:
properties:
sdp:
type: string
type:
example: answer
type: string
type: object
host: localhost:8080
info:
contact:
name: x0gp
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.
Auth is intentionally disabled in PoC mode (X0GP_DEV_MODE=true). All catalog mutations are allowed without a JWT.
license:
name: MIT
title: x0gp PoC server API
version: 0.1.0-poc
paths:
/api/cars:
get:
description: 'Returns all cars visible to the caller. Optional query filters:
`scope=system|public|private`, `owner_id=<string>`.'
parameters:
- description: Filter by visibility
enum:
- system
- public
- private
in: query
name: scope
type: string
- description: Filter by owner id
in: query
name: owner_id
type: string
produces:
- application/json
responses:
"200":
description: List of cars with count
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: List cars
tags:
- cars
post:
consumes:
- application/json
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).
parameters:
- description: Car to create
in: body
name: car
required: true
schema:
$ref: '#/definitions/transport.CarCreateRequest'
produces:
- application/json
responses:
"201":
description: Created
schema:
$ref: '#/definitions/transport.CarAck'
"400":
description: Invalid input
schema:
additionalProperties: true
type: object
"409":
description: Car id already exists
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Create a car
tags:
- cars
/api/cars/{id}:
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.
parameters:
- description: Car id
in: path
name: id
required: true
type: string
- description: Patch payload (only for PUT)
in: body
name: car
schema:
$ref: '#/definitions/transport.CarUpdateRequest'
produces:
- application/json
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:
additionalProperties: true
type: object
"403":
description: System car is immutable
schema:
additionalProperties: true
type: object
"404":
description: Car not found
schema:
additionalProperties: true
type: object
"409":
description: Car id already exists
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Get / update / delete a car by id
tags:
- cars
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.
parameters:
- description: Car id
in: path
name: id
required: true
type: string
- description: Patch payload (only for PUT)
in: body
name: car
schema:
$ref: '#/definitions/transport.CarUpdateRequest'
produces:
- application/json
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:
additionalProperties: true
type: object
"403":
description: System car is immutable
schema:
additionalProperties: true
type: object
"404":
description: Car not found
schema:
additionalProperties: true
type: object
"409":
description: Car id already exists
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Get / update / delete a car by id
tags:
- cars
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.
parameters:
- description: Car id
in: path
name: id
required: true
type: string
- description: Patch payload (only for PUT)
in: body
name: car
schema:
$ref: '#/definitions/transport.CarUpdateRequest'
produces:
- application/json
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:
additionalProperties: true
type: object
"403":
description: System car is immutable
schema:
additionalProperties: true
type: object
"404":
description: Car not found
schema:
additionalProperties: true
type: object
"409":
description: Car id already exists
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Get / update / delete a car by id
tags:
- cars
/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
responses:
"200":
description: Snapshot of tracks, cars and summary counters
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Combined catalog snapshot
tags:
- catalog
/api/clans:
get:
description: Returns clans ordered by tag. Limit 1..200, default 50.
parameters:
- description: Page size
in: query
name: limit
type: integer
- description: Page offset
in: query
name: offset
type: integer
produces:
- application/json
responses:
"200":
description: Page of clans
schema:
$ref: '#/definitions/transport.ClanListResponse'
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: List clans
tags:
- clans
post:
consumes:
- application/json
description: Creates a new clan. `tag` must be exactly 3 uppercase ASCII letters
(e.g. "ACE") and unique.
parameters:
- description: Clan to create
in: body
name: clan
required: true
schema:
$ref: '#/definitions/transport.ClanCreateRequest'
produces:
- application/json
responses:
"201":
description: Created clan
schema:
$ref: '#/definitions/transport.Clan'
"400":
description: Invalid input (tag must be 3 uppercase letters)
schema:
additionalProperties: true
type: object
"409":
description: Clan with that tag already exists
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Create a clan
tags:
- clans
/api/clans/{id}:
delete:
description: Dispatches on HTTP method.
parameters:
- description: Clan id
in: path
name: id
required: true
type: string
- description: Patch payload (only for PUT)
in: body
name: clan
schema:
$ref: '#/definitions/transport.ClanUpdateRequest'
produces:
- application/json
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:
additionalProperties: true
type: object
"404":
description: Clan not found
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Get / update / delete a clan by id
tags:
- clans
get:
description: Dispatches on HTTP method.
parameters:
- description: Clan id
in: path
name: id
required: true
type: string
- description: Patch payload (only for PUT)
in: body
name: clan
schema:
$ref: '#/definitions/transport.ClanUpdateRequest'
produces:
- application/json
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:
additionalProperties: true
type: object
"404":
description: Clan not found
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Get / update / delete a clan by id
tags:
- clans
put:
description: Dispatches on HTTP method.
parameters:
- description: Clan id
in: path
name: id
required: true
type: string
- description: Patch payload (only for PUT)
in: body
name: clan
schema:
$ref: '#/definitions/transport.ClanUpdateRequest'
produces:
- application/json
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:
additionalProperties: true
type: object
"404":
description: Clan not found
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Get / update / delete a clan by id
tags:
- clans
/api/drivers:
get:
description: Returns drivers ordered by nickname. Optional `clan_id` filter.
parameters:
- description: Page size
in: query
name: limit
type: integer
- description: Page offset
in: query
name: offset
type: integer
- description: Filter by clan id
in: query
name: clan_id
type: string
produces:
- application/json
responses:
"200":
description: Page of drivers
schema:
$ref: '#/definitions/transport.DriverListResponse'
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: List drivers
tags:
- drivers
post:
consumes:
- application/json
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.
parameters:
- description: Driver to create
in: body
name: driver
required: true
schema:
$ref: '#/definitions/transport.DriverCreateRequest'
produces:
- application/json
responses:
"201":
description: Created driver
schema:
$ref: '#/definitions/transport.Driver'
"400":
description: Invalid input (nickname must be 3 uppercase letters)
schema:
additionalProperties: true
type: object
"409":
description: Driver with that nickname already exists
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Create a driver
tags:
- drivers
/api/drivers/{id}:
delete:
description: Dispatches on HTTP method. Path `/api/drivers/by-nick/{nick}` is
a convenience that returns the driver by 3-letter nickname.
parameters:
- description: Driver id
in: path
name: id
required: true
type: string
- description: Patch payload (only for PUT)
in: body
name: driver
schema:
$ref: '#/definitions/transport.DriverUpdateRequest'
produces:
- application/json
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:
additionalProperties: true
type: object
"404":
description: Driver not found
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Get / update / delete a driver by id
tags:
- drivers
get:
description: Dispatches on HTTP method. Path `/api/drivers/by-nick/{nick}` is
a convenience that returns the driver by 3-letter nickname.
parameters:
- description: Driver id
in: path
name: id
required: true
type: string
- description: Patch payload (only for PUT)
in: body
name: driver
schema:
$ref: '#/definitions/transport.DriverUpdateRequest'
produces:
- application/json
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:
additionalProperties: true
type: object
"404":
description: Driver not found
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Get / update / delete a driver by id
tags:
- drivers
put:
description: Dispatches on HTTP method. Path `/api/drivers/by-nick/{nick}` is
a convenience that returns the driver by 3-letter nickname.
parameters:
- description: Driver id
in: path
name: id
required: true
type: string
- description: Patch payload (only for PUT)
in: body
name: driver
schema:
$ref: '#/definitions/transport.DriverUpdateRequest'
produces:
- application/json
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:
additionalProperties: true
type: object
"404":
description: Driver not found
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Get / update / delete a driver by id
tags:
- drivers
/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.
parameters:
- description: Driver id
in: path
name: id
required: true
type: string
- description: Patch payload (only for PUT)
in: body
name: driver
schema:
$ref: '#/definitions/transport.DriverUpdateRequest'
produces:
- application/json
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:
additionalProperties: true
type: object
"404":
description: Driver not found
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Get / update / delete a driver by id
tags:
- drivers
/api/leaderboard:
get:
description: Returns paginated leaderboard list. Supports overall leaderboard
or track-specific leaderboard for the current year.
parameters:
- description: Track ID. If empty, returns overall leaderboard.
in: query
name: track_id
type: string
- description: Year. Defaults to current year.
in: query
name: year
type: integer
- description: Limit (1..200, default 50)
in: query
name: limit
type: integer
- description: Offset (default 0)
in: query
name: offset
type: integer
- description: Current driver ID. Defaults to driver-seed-001 (mock Alice).
in: header
name: X-Driver-Id
type: string
produces:
- application/json
responses:
"200":
description: Leaderboard page and current driver position
schema:
$ref: '#/definitions/transport.LeaderboardResponse'
"400":
description: Invalid input
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Leaderboard
tags:
- leaderboard
/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.
parameters:
- description: Year. Defaults to current year.
in: query
name: year
type: integer
- description: Current driver ID. Defaults to driver-seed-001 (mock Alice).
in: header
name: X-Driver-Id
type: string
produces:
- application/json
responses:
"200":
description: Calendar leaderboards list
schema:
$ref: '#/definitions/transport.TrackCalendarLeaderboardResponse'
"400":
description: Invalid input
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Track Calendar Leaderboards
tags:
- leaderboard
/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.
## Filters
- `status` (comma-separated, e.g. "racing,finished"). Recognised values: all, lobby, racing, finished.
- `track_id` (exact match against a physical track id; custom lobbies are not supported in this build).
- `limit` (1..200, default 50)
parameters:
- description: Comma-separated status filter
enum:
- all
- lobby
- racing
- finished
in: query
name: status
type: string
- description: Filter by physical track id
in: query
name: track_id
type: string
- description: Opaque keyset cursor (next_cursor from previous page)
in: query
name: cursor
type: string
- description: Page size (1..200, default 50)
in: query
name: limit
type: integer
produces:
- application/json
responses:
"200":
description: Page of races
schema:
$ref: '#/definitions/transport.RaceListResponse'
"400":
description: Bad request / invalid cursor / invalid status
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: List races (keyset paginated)
tags:
- races
/api/races/plans:
get:
description: Returns race plans in ascending start_at order. Pass back the `next_cursor`
to get the next page.
parameters:
- description: Opaque keyset cursor
in: query
name: cursor
type: string
- description: Page size (default 50)
in: query
name: limit
type: integer
produces:
- application/json
responses:
"200":
description: Page of plans
schema:
$ref: '#/definitions/transport.RacePlanListResponse'
"400":
description: Invalid cursor
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: List race plans (keyset paginated)
tags:
- plans
post:
consumes:
- application/json
description: Creates a recurring or one-off scheduled race. The scheduler materialises
a lobby race when `next_fire_ms` <= now. If `interval_s` > 0 the plan repeats;
if `count` > 0 the plan disables itself after that many fires.
parameters:
- description: Plan to create
in: body
name: plan
required: true
schema:
$ref: '#/definitions/transport.RacePlanCreateRequest'
produces:
- application/json
responses:
"201":
description: Created plan
schema:
$ref: '#/definitions/transport.RacePlan'
"400":
description: Invalid input
schema:
additionalProperties: true
type: object
"409":
description: Plan id already exists
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Create a race plan
tags:
- plans
/api/races/plans/{id}:
delete:
description: Removes a plan by id. Materialised races already in the lobby are
not affected.
parameters:
- description: Plan id
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: ok
schema:
additionalProperties: true
type: object
"404":
description: Plan not found
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Delete a race plan
tags:
- plans
/api/races/queue:
delete:
description: Removes a single (driver, race) entry from the queue. Does not
detach the driver from the race if already joined.
parameters:
- description: Driver id
in: header
name: X-Driver-Id
type: string
- description: Driver id
in: query
name: driver_id
type: string
- description: Race id
in: query
name: race_id
required: true
type: string
produces:
- application/json
responses:
"200":
description: ok
schema:
additionalProperties: true
type: object
"400":
description: driver_id and race_id required
schema:
additionalProperties: true
type: object
"404":
description: queue entry not found
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Leave a queue entry
tags:
- races
get:
description: Returns the queue entries for a driver, oldest first.
parameters:
- description: Driver id
in: header
name: X-Driver-Id
type: string
- description: Driver id (alternative to header)
in: query
name: driver_id
type: string
produces:
- application/json
responses:
"200":
description: Queue entries
schema:
$ref: '#/definitions/transport.RaceQueueListResponse'
"400":
description: driver_id required
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: List driver's queue entries
tags:
- races
/api/races/queue/join:
post:
consumes:
- application/json
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).
parameters:
- description: Driver id (alternative to body)
in: header
name: X-Driver-Id
type: string
- description: Driver id and optional limit
in: body
name: body
schema:
$ref: '#/definitions/transport.RaceQueueJoinRequest'
produces:
- application/json
responses:
"200":
description: Queue entries created
schema:
$ref: '#/definitions/transport.RaceQueueJoinResponse'
"400":
description: driver_id required
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Enqueue driver on next N upcoming races
tags:
- races
/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.
parameters:
- description: Number of races to return (1..16, default 3)
in: query
name: limit
type: integer
produces:
- application/json
responses:
"200":
description: Upcoming races
schema:
$ref: '#/definitions/transport.RaceUpcomingResponse'
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Next N upcoming races
tags:
- races
/api/tracks:
get:
description: 'Returns all tracks visible to the caller. Optional query filters:
`scope=system|public|private`, `tag=<string>`.'
parameters:
- description: Filter by visibility
enum:
- system
- public
- private
in: query
name: scope
type: string
- description: Filter by tag (case-insensitive)
in: query
name: tag
type: string
produces:
- application/json
responses:
"200":
description: List of tracks with count
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: List tracks
tags:
- tracks
post:
consumes:
- application/json
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).
parameters:
- description: Track to create
in: body
name: track
required: true
schema:
$ref: '#/definitions/transport.TrackCreateRequest'
produces:
- application/json
responses:
"201":
description: Created
schema:
$ref: '#/definitions/transport.TrackAck'
"400":
description: Invalid input
schema:
additionalProperties: true
type: object
"409":
description: Track id already exists
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Create a track
tags:
- tracks
/api/tracks/{id}:
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.
parameters:
- description: Track id
in: path
name: id
required: true
type: string
- description: Patch payload (only for PUT)
in: body
name: track
schema:
$ref: '#/definitions/transport.TrackUpdateRequest'
produces:
- application/json
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:
additionalProperties: true
type: object
"403":
description: System track is immutable
schema:
additionalProperties: true
type: object
"404":
description: Track not found
schema:
additionalProperties: true
type: object
"409":
description: Track id already exists
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Get / update / delete a track by id
tags:
- tracks
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.
parameters:
- description: Track id
in: path
name: id
required: true
type: string
- description: Patch payload (only for PUT)
in: body
name: track
schema:
$ref: '#/definitions/transport.TrackUpdateRequest'
produces:
- application/json
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:
additionalProperties: true
type: object
"403":
description: System track is immutable
schema:
additionalProperties: true
type: object
"404":
description: Track not found
schema:
additionalProperties: true
type: object
"409":
description: Track id already exists
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Get / update / delete a track by id
tags:
- tracks
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.
parameters:
- description: Track id
in: path
name: id
required: true
type: string
- description: Patch payload (only for PUT)
in: body
name: track
schema:
$ref: '#/definitions/transport.TrackUpdateRequest'
produces:
- application/json
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:
additionalProperties: true
type: object
"403":
description: System track is immutable
schema:
additionalProperties: true
type: object
"404":
description: Track not found
schema:
additionalProperties: true
type: object
"409":
description: Track id already exists
schema:
additionalProperties: true
type: object
"500":
description: Internal server error
schema:
additionalProperties: true
type: object
summary: Get / update / delete a track by id
tags:
- tracks
/api/tracks/calendar:
get:
description: Returns the full calendar of track active periods.
produces:
- application/json
responses:
"200":
description: List of scheduled active periods
schema:
$ref: '#/definitions/transport.TrackCalendarResponse'
"500":
description: Internal error
schema:
additionalProperties: true
type: object
summary: Get Track Calendar
tags:
- tracks
post:
consumes:
- application/json
description: Schedules an active period for a physical track.
parameters:
- description: Calendar entry details
in: body
name: payload
required: true
schema:
$ref: '#/definitions/transport.TrackCalendarCreateRequest'
produces:
- application/json
responses:
"201":
description: Created calendar entry
schema:
$ref: '#/definitions/transport.TrackCalendarEntry'
"400":
description: Invalid input
schema:
additionalProperties: true
type: object
"404":
description: Track not found
schema:
additionalProperties: true
type: object
"500":
description: Internal error
schema:
additionalProperties: true
type: object
summary: Schedule Track Active Period
tags:
- tracks
/api/tracks/calendar/{id}:
delete:
description: Deletes a scheduled active period by ID.
parameters:
- description: Calendar Entry ID
in: path
name: id
required: true
type: integer
responses:
"204":
description: No Content
"404":
description: Not found
schema:
additionalProperties: true
type: object
"500":
description: Internal error
schema:
additionalProperties: true
type: object
summary: Delete Scheduled Period
tags:
- tracks
/api/version:
get:
description: Returns the current server version.
produces:
- application/json
responses:
"200":
description: Success
schema:
$ref: '#/definitions/transport.VersionResponse'
summary: Get server version
tags:
- system
/api/video/control:
post:
description: Sends a text command (e.g. start, stop, left, right) to the ESP32
command port (UDP 8888).
parameters:
- description: Device ID (0..127)
in: query
name: id
type: integer
- description: Command text
in: query
name: cmd
required: true
type: string
produces:
- application/json
responses:
"200":
description: Command sent
schema:
additionalProperties: true
type: object
"400":
description: Missing cmd parameter
schema:
type: string
"500":
description: Internal error
schema:
type: string
summary: Send command to ESP32
tags:
- video
/api/video/stream:
get:
description: Streams video frames received via UDP as multipart/x-mixed-replace.
Suitable for raw img tags in browser.
parameters:
- description: Device ID (0..127)
in: query
name: id
type: integer
produces:
- multipart/x-mixed-replace
responses:
"200":
description: MJPEG stream
schema:
type: string
summary: Get MJPEG video stream from ESP32
tags:
- video
/api/webrtc/connect:
post:
consumes:
- application/json
description: Accepts SDP Offer, negotiates peer connection, and returns SDP
Answer. Routes video and control data via DataChannels.
parameters:
- description: SDP Offer details
in: body
name: body
required: true
schema:
$ref: '#/definitions/transport.WebRTCConnectRequest'
produces:
- application/json
responses:
"200":
description: SDP Answer negotiation success
schema:
$ref: '#/definitions/transport.WebRTCConnectResponse'
summary: Establish WebRTC connection for driver-car 1-to-1 proxying
tags:
- webrtc
/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
responses:
"200":
description: Server is up
schema:
additionalProperties: true
type: object
summary: Liveness probe
tags:
- system
/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
responses:
"200":
description: Runtime counters
schema:
additionalProperties: true
type: object
summary: Realtime runtime stats
tags:
- system
/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`.
## Frame format
Every frame is a JSON envelope of the form:
```json
{ "type": "<message_type>", "seq": 42, "ts_ms": 1700000000000, "payload": { ... } }
```
`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).
## Client -> Server
- `client_hello` — first frame after connect. Sent once.
- `join_race` / `leave_race` — join or leave the race session.
- `input_state` — high-rate control (steering/throttle/brake/gear). Send at 60-120 Hz.
- `ping` — round-trip time probe.
- `track_*` / `car_*` — catalog CRUD (mirror of the REST /api/* endpoints).
- `lobby_*` — lobby management.
- `stats_request` — ask for a stats snapshot.
## Server -> Client
- `server_hello` — handshake reply; includes session id and engine config.
- `race_snapshot` — periodic authoritative state broadcast.
- `input_ack` — per-input acknowledgement (optional; drop-tolerant).
- `race_event` — start/lap/sector/dnf/finish.
- `track_snapshot` / `car_snapshot` / `catalog_summary` — catalog feeds on change.
- `lobby_snapshot` — full lobby state on change.
- `stats_snapshot` / `race_standings` / `lap_recorded` / `race_result` — stats feeds.
- `error` — protocol or validation error.
## Notes
- Origin checks are disabled in PoC mode (`CheckOrigin: always-allow`).
- Ping interval: 20 s. Pong wait: 60 s. Max frame size: 8 KiB.
- Input acks are best-effort and silently dropped under load.
responses:
"101":
description: Switching protocols. Subsequent frames follow the envelope
schema.
schema:
$ref: '#/definitions/transport.Envelope'
summary: WebSocket endpoint (binary JSON envelope protocol)
tags:
- websocket
schemes:
- http
- ws
swagger: "2.0"