# x0gp — Server Plan (Go) Документ фиксирует серверную архитектуру: транспорт, модули, протокол, хранение, биллинг, деплой. Дата фиксации архитектурных решений: 2026-06-18. Дата обновления (домашний сервер для PoC и MVP): 2026-06-18. Дата обновления v2 (Orange Pi 5 Pro + Coolify + статический IP): 2026-06-18. Архитектурные выборы, на которых основан план: 1. Транспорт клиент-сервер: **WebSocket + бинарный protobuf**. 2. Управление машинками: **Wi-Fi, ESP32-S3 агент**. 3. Модель доверия: **authoritative server + client-side prediction**. 4. Позиционирование: **IR-магнитные секторы + overhead-CV (гибрид)**. --- ## 1. Цели и не-цели ### 1.1. Цели - Round-trip "команда на экране -> отклик машинки" в худшем случае 200 мс, в типичном 80-120 мс. - Поддержка одновременно 50-100 активных заездов на 1 Edge-инстанс. - Устойчивость к обрывам связи (клиент теряет сеть на 0.5-2 с без потери состояния). - Anti-cheat: защита от подмены телеметрии, авто-команд, грязного руления. - Биллинг заездов и турниров (ЮKassa для РФ, Stripe для остального). - Горизонтальное масштабирование (stateless API, sticky-сессии на WS). ### 1.2. Не-цели (для MVP) - Физика шин (скорость не считаем, считаем положение с CV). - Запись/воспроизведение VR-реплеев. - Полноценный matchmaking с ELO (только базовый лобби). - Мобильное приложение (только Web, мобилка во второй очереди). --- ## 2. Высокоуровневая архитектура ``` +----------------------+ | React (Web client) | | React Native (mobile)| +----------+-----------+ | WebSocket (binary protobuf) | +----------v-----------+ | Edge / API Gateway | | (Go, stateless) | +----+------+-----+----+ | | | +-------------+ | +-------------+ | | | +------v------+ +--------v-------+ +------v------+ | Race Engine | | Realtime Hub | | REST API | | (stateful) | | (WS fan-out) | | (chi/echo) | +-------------+ +----------------+ +-------------+ | | | +---------+---------+ | | | +--------v--------+ | | Redis | pub/sub, state | +-----------------+ | | | +--------v--------+ | | Postgres / TS |<-------------------+ +-----------------+ ``` **Edge / API Gateway** — единая точка входа, TLS, rate-limit, auth, маршрутизация. **Race Engine** — authoritative state гонки, tick 60 Hz. **Realtime Hub** — WebSocket-концентратор, fan-out игрокам, сериализация. **REST API** — управление пользователями, лобби, платежами, история. --- ## 3. Транспорт ### 3.1. WebSocket (основной) - Библиотека: **gorilla/websocket** или **nbio** (последняя — non-blocking, лучше для тысяч соединений). - Формат кадров: **бинарный protobuf** (для latency) + опционально JSON для не-real-time сообщений (чат лобби). - Subprotocol: `x0gp.v1`. - Ping/pong: каждые 5 с, читаем RTT для диагностики. ### 3.2. Альтернатива на будущее - WebRTC DataChannel — если потребуется суб-50 мс jitter. - Собственный UDP+KCP — если появится конкурентная потребность. - В плане не реализуем, но держим "port" в протоколе для миграции. ### 3.3. REST API - HTTP/2, chi router. - OpenAPI 3.1 спецификация, кодогенерация клиента. - Авторизация: JWT (access 15 мин) + refresh token (30 дней). - Idempotency-Key для платёжных эндпоинтов. --- ## 4. Структура Go-модулей ``` server/ cmd/ api-gateway/ # REST + WS, stateless race-engine/ # stateful, authoritative tick agent-linker/ # запускается в помещении (на gateway-ПК) indexer/ # пишет телеметрию в Timescale replay-recorder/ # (опц.) пишет replay в S3 internal/ auth/ # JWT, сессии, refresh, MFA billing/ # ЮKassa/Stripe, биллинг сессий race/ # сессии, круги, DNF, рестарты, секторы realtime/ # WS hub, fan-out, per-client очереди transport/ # protobuf-схемы, кодеки control/ # приём команд, rate-limit, prediction snapshots prediction/ # client-side prediction серверные корректировки telemetry/ # агрегация, пересылка в TS anti_cheat/ # эвристики: sanity check, повторы, geo tournament/ # bracket, лэддер, регистрация storage/ postgres/ # sqlx/pgx, миграции redis/ # сессии, pub/sub, лобби timescale/ # телеметрия obs/ # логирование, метрики, трейсы proto/ client_server.proto # команды, телеметрия для игрока server_agent.proto # команды и состояние для машинки api/ openapi.yaml migrations/ # SQL deploy/ docker-compose.yml # dev helm/ # prod scripts/ loadtest/ # 100 виртуальных гонщиков ``` --- ## 5. Протокол (protobuf) ### 5.1. Клиент -> Сервер - `ClientHello { version, token, locale }` - `JoinRace { raceId, carClass }` - `LeaveRace {}` - `InputState { seq, tsMs, steering (-1..1), throttle (0..1), brake (0..1), gear (-1..0..1), buttons }` (60-120 Hz) - `ChatMessage { text }` (rate-limited) - `Ping { tsMs }` ### 5.2. Сервер -> Клиент - `ServerHello { sessionId, raceTick, config }` - `RaceSnapshot { tick, tsMs, cars: [{id, x, y, heading, speed, lap, sector, lastLapMs}], leaderboard }` (30-60 Hz) - `InputAck { seq, appliedAtMs }` - `Correction { deltaSteering, deltaThrottle, reason }` (редко, при расхождении) - `RaceEvent { type: START|LAP|SECTOR|DNF|END, payload }` - `TournamentUpdate { ... }` - `Pong { tsMs }` ### 5.3. Сервер -> Бортовой агент - `AgentCommand { carId, steering, throttle, brake, gear, flags, expiresAtMs }` (60-120 Hz) - `AgentConfig { maxSteeringRate, maxThrottleRate, rampLimits }` ### 5.4. Бортовой агент -> Сервер - `AgentHello { carId, fwVersion, hwId, rssi }` - `AgentTelemetry { carId, tsMs, steeringActual, throttleActual, rpm, voltage, current, tempEsc, tempMotor, imu: {ax, ay, az, gx, gy, gz}, irSensor: {front, rear}, batteryPct }` (10-30 Hz) - `Heartbeat { tsMs, uptimeSec, errors }` (1 Hz) - `AgentLog { level, msg }` ### 5.5. Gateway-агент -> Edge-сервер - `TrackHello { trackId, hwId, publicKey, version }` - `CarFrame { carId, tsMs, position: {x,y,z}, heading, velocity, confidence }` (из CV, 30-60 Hz) - `SectorEvent { carId, sector, tsMs, lap }` - `ReplayChunk { raceId, tsMs, jpegBytes }` (по запросу) - `LocalCommand { carId, payload }` (forwarded from server) --- ## 6. Race Engine (authoritative tick) ### 6.1. Tick - Фиксированный шаг: **16.67 мс (60 Hz)**. - На каждом тике: 1. Принять все InputState от игроков за последние N мс (батч). 2. Применить к authoritative state машинок (с учётом ramp limits). 3. Сформировать команды бортовым агентам. 4. Отправить команды агенту в помещении. 5. Через ~33 мс получить телеметрию и CV-обновления. 6. Скорректировать state. 7. Сформировать RaceSnapshot для клиентов. ### 6.2. Client-side prediction - Клиент хранит последний применённый state и локально симулирует физику между snapshot-ами. - При получении Snapshot клиент сверяет, если расхождение > порога — плавная коррекция (lerp 100-200 мс), без "телепорта". - Сервер шлёт `Correction` только если расхождение > критического. ### 6.3. Anti-cheat эвристики - Sanity check входящих команд (steering/throttle в допустимом диапазоне, скорость изменения не выше физического предела). - Сравнение CV-позиции с заявленной (на сервере) — если расходятся > N метров, пометка DQF. - Повторы replay: после заезда сверяем логгированные команды с CV-треком. - Device fingerprint + поведенческий профиль. - Rate limit: > 200 Hz InputState = бан. ### 6.4. Состояния гонки - `LOBBY` -> `COUNTDOWN` (3..1) -> `RACING` -> `FINISHED`. - DNF по: таймаут без input 5 с, потеря связи с агентом > 3 с, флаг администратора. - RESTART: только на LoBBY или между раундами. --- ## 7. Realtime Hub - Один WS-коннект на игрока. - Подписки: `race:{raceId}`, `tournament:{id}`, `user:{id}`. - Fan-out: при Snapshot race-движок публикует в Redis pub/sub, Hub читает и шлёт подписанным клиентам. - Backpressure: если клиент не успевает читать — дропаем старые Snapshot, шлём только последний. - Sticky-сессии: cookie `x0gp_sticky` при балансировке на L7. --- ## 8. Хранение ### 8.1. PostgreSQL (relational) Таблицы (основные): - `users` (id, email, phone, password_hash, created_at, locale) - `sessions` (id, user_id, refresh_hash, ip, ua, expires_at) - `wallets` (user_id, balance, currency) - `payments` (id, user_id, provider, ext_id, amount, status, created_at) - `races` (id, track_id, class, started_at, ended_at, winner_id, replay_url) - `race_results` (race_id, user_id, car_id, lap_times[], best_lap, dnf_reason) - `tournaments` (id, name, format, entry_fee, prize_pool, status) - `tournament_entries` (tournament_id, user_id, seed, eliminated_at) - `cars` (id, hw_id, model, fw_version, status, last_seen) - `tracks` (id, name, layout_id, geo_city, status) Миграции: `golang-migrate/migrate`. ### 8.2. Redis - `session:{token}` -> userId (TTL 15 мин) - `refresh:{token}` -> userId (TTL 30 дн) - `race:{id}:state` -> serialized race state (TTL 1 час) - `lobby:{id}` -> set of userIds - `ratelimit:{user}:{op}` -> token bucket - pub/sub channel `race_events` ### 8.3. TimescaleDB (time-series) - hypertable `telemetry` (ts, race_id, car_id, field, value) - 7 дней горячих данных, потом cold в S3. - continuous aggregates для "лучший круг на треке", "топ-скорости". --- ## 9. Биллинг ### 9.1. Провайдеры - **ЮKassa** — для РФ. - **Stripe** — для остального мира (фаза 3). ### 9.2. Сценарии - Пополнение кошелька (one-time payment). - Списание за заезд (раз в N минут или за сессию). - Входной взнос в турнир. - Призовые начисления победителям. ### 9.3. Идемпотентность и реконсиляция - Каждый платёж имеет `Idempotency-Key` на стороне клиента и `ext_id` от провайдера. - Webhook-эндпоинт `/webhooks/yookassa` с проверкой подписи. - Ежедневный job сверяет балансы с провайдером. --- ## 10. Авторизация - Email + password (argon2id) — базовый сценарий. - OAuth Yandex / VK / Google — для удобства. - MFA TOTP — обязательно для админов и pro-игроков. - JWT: HS256 в PoC, RS256 в проде. - Refresh-token rotation, device-binding. --- ## 11. Anti-cheat Уровни: 1. **Sanity** — диапазоны, скорости, ramp limits (всегда). 2. **CV-check** — расхождение с overhead-камерой > 1 м -> пометка. 3. **Replay verify** — после заезда можно пересчитать (асинхронно). 4. **Behavioral** — паттерны (слишком "идеальные" круги = подозрительно). 5. **Manual review** — оператор смотрит replay при споре. --- ## 12. Observability - Логи: `slog` (stdlib) в JSON, отправка в Loki. - Метрики: `prometheus/client_golang`, экспорт `/metrics`. - Трейсы: OpenTelemetry, OTLP в Jaeger/Tempo. - Ключевые метрики: - `ws_connections_total` - `rtt_ms_p50/p95/p99` (по `(clientSendTs - serverRecvTs)`) - `tick_lag_ms` (если тик опаздывает > 5 мс — алерт). - `input_rate_per_client` - `dropped_snapshots_per_client` - `gateway_online{trackId}` (heartbeat агенты в помещении). --- ## 13. Деплой и инфраструктура **Базовое решение: PoC и MVP крутятся на Orange Pi 5 Pro через Coolify.** Перенос на облако/VPS — когда появится коммерческая нагрузка и SLA обязательства. ### 13.1. Orange Pi 5 Pro (PoC и MVP) **Платформа: Orange Pi 5 Pro** (Rockchip RK3588S, ARM64). Уже есть в наличии. **Характеристики:** - CPU: 8 cores (4× Cortex-A76 @ 2.4 GHz + 4× Cortex-A55 @ 1.8 GHz) - RAM: 4 / 8 / 16 / 32 GB LPDDR4x (зависит от версии) - GPU: Mali-G610 MP4 - **NPU: 6 TOPS** (RKNN) — для CV-инференса (YOLO/ByteTrack) **на борту** - Сеть: 2× 2.5 GbE (один WAN, один LAN с трассой — **физическая изоляция**) - Wi-Fi 6 (резерв) - USB 3.0 × 2, PCIe 2.1 x1, SATA - ОС: Ubuntu (уже стоит, с **Coolify**) **Coolify как платформа деплоя:** - Self-hosted PaaS, уже развёрнут. - UI для управления сервисами, доменами, SSL, env-vars. - Push-to-deploy из Git-репо (GitHub/Gitea). - Сервисы = Docker Compose под капотом, но с UI и без боли. - Встроенные: PostgreSQL, Redis, MinIO (S3-совместимое), Plausible, Uptime Kuma. - Версионирование деплоев, rollback в 1 клик. **Доступ из интернета (статический IP уже есть):** - **Основной вариант**: Caddy reverse proxy + Let's Encrypt напрямую. Coolify умеет провизить LE-сертификаты из коробки. - Подходит, т.к. провайдер даёт статический IP, 443/tcp открыт на роутере через port-forward на Orange Pi. - Cloudflare Tunnel не нужен (нет смысла добавлять лишний hop). - WAF: Crowdsec через Coolify (опц., можно отложить). **Архитектура:** ``` +---------------- ORANGE PI 5 PRO (Ubuntu + Coolify) ----------------+ | | | Coolify (docker compose orchestration) | | +-- api-gateway ------------------------------------+ | | +-- race-engine ----------------------------------+ | | +-- postgres+timescale (через Coolify UI) | | | +-- redis -----------------------------------+ | | +-- caddy (LE) -----------------------------------+ | | +-- prometheus+grafana -----------------------------------+ | | +-- minio (S3 replays) -----------------------------------+ | | | | Natively (systemd) - требуют прямой доступ к железу: | | +-- agent-linker (Wi-Fi к машинкам) | | +-- cv-tracker (USB камеры, NPU RKNN) | | +-- node_exporter (мониторинг) | | | +---------------------------------------------------------------------+ 2.5 GbE #1 (WAN, к роутеру) 2.5 GbE #2 (LAN к трассе) | | vv +--------------+ +---------------------+ | Internet | | Switch (или точка- | | (clients) | | точка) | +--------------+ +----------+----------+ | +------v------+ | x0gp-track | | AP + камеры | +-------------+ ``` **Изоляция сети трассы:** - LAN-порт Orange Pi → выделенный коммутатор (или напрямую к точке доступа x0gp-track). - В этой сети только машинки и камеры — никаких домашних устройств. - AP настраивается на изолированный SSID / VLAN. - Это даёт предсказуемый latency (нет соседского трафика) и безопасность. **Использование NPU RK3588S:** - RKNN Toolkit 2 для конвертации моделей (PyTorch → ONNX → RKNN). - Поддерживаются YOLOv8/v9, ByteTrack через onnxruntime-rknn. - Целевой инференс: 60-120 FPS на одной камере 1080p (с запасом). - Если NPU не подходит — fallback на CPU ARM A76. - Альтернативный inference-сервер: Ultralytics + onnxruntime-rknn. **ARM64-специфика:** - Docker-образы должны быть `linux/arm64` или `multi-arch`. - Postgres+TimescaleDB, Redis, Caddy, Prometheus, Grafana — есть для arm64. - Go-сервисы — `GOARCH=arm64` при сборке (по умолчанию если собирать на Orange Pi). - CV-инференс на Python — pin версии onnxruntime-rknn под aarch64. **Бэкапы:** - Ежедневный `pg_dump` (cron в Coolify или отдельный scheduled task). - Backup в MinIO (S3-совместимое) внутри Coolify. - Опционально off-site: rclone → Backblaze B2 / Yandex Object Storage. - Retention: 14 дней локально, 30 дней off-site. **Безопасность:** - SSH: только ключи, fail2ban. - Coolify UI: за reverse proxy с basic auth или Cloudflare Access. - Порты наружу: 80/tcp + 443/tcp (для Caddy/LE). - Postgres и Redis слушают только 127.0.0.1 внутри Coolify. - UPS для Orange Pi (microSD карты не любят внезапные отключения). ### 13.2. Production (после MVP, когда появится коммерческая нагрузка) - VPS в Москве и СПб (2 региона) — Selectel / Yandex Cloud / Timeweb. - Managed Postgres + Redis (для снятия с Orange Pi задач бэкапа и HA). - Kubernetes (Yandex Cloud / Selectel / AWS) — HPA по метрикам ws_connections, tick_lag. - Redis Cluster, Postgres с read-replica. - CDN для статики (CloudFront / Cloudflare). - WAF (CrowdSec). - Orange Pi остаётся как **gateway-агент + CV-инференс** в помещении с трассой (NPU продолжаем использовать для позиционирования). - Edge-сервер (Go) переезжает на VPS, к которому подключаются несколько Orange Pi из разных помещений. --- ## 14. Производительность и SLO | Метрика | Цель | |---|---| | API p99 latency | < 100 мс | | WS input->ack | < 30 мс на Edge | | Tick stability | 60 Hz +/- 2 мс | | Один race-engine | 100 активных гонок | | Один api-gateway | 5000 WS-клиентов | | Доступность | 99.5% (PoC), 99.9% (prod) | --- ## 15. Roadmap (server) | Фаза | Срок | Что делаем | |---|---|---| | **S0: PoC моно-сервис** | 1-2 нед | Один Go-бинарь: WS + REST + race state in-memory + локальный agent-linker. SQLite. Без биллинга. | | **S1: MVP модульный** | 4-6 нед | Разделение на api-gateway / race-engine / agent-linker. Postgres + Redis. Auth, JWT, лобби. | | **S2: CV + телеметрия** | 8-10 нед | Интеграция с overhead-CV. TimescaleDB. Replay recorder (S3-совместимое). | | **S3: Биллинг** | 10-12 нед | ЮKassa, кошелёк, сессии под оплатой. Webhooks. Админка. | | **S4: Турниры** | 14-18 нед | Bracket, лэддер, призовые. Анти-чит уровня 2-3. | | **S5: Продакшн** | 4-6 мес | K8s, мульти-регион, SLO, on-call. Stripe для остального мира. Мобильное приложение. | --- ## 16. Открытые вопросы - **Стек frontend**: React + Vite, какие библиотеки для WS (reconnect, backoff)? Кандидаты: `reconnecting-websocket`, `socket.io-client` (overkill, но быстро), свой тонкий клиент на `WebSocket` API. - **Cloudflare Tunnel vs WireGuard vs статический IP** — что выберем для доступа к домашнему серверу? Дефолт: Cloudflare Tunnel (проще, есть free-tier, базовый WAF). Уточним после тестов latency. - **Бэкапы Postgres на NAS** — есть ли дома NAS или использовать внешнее S3-совместимое (Backblaze B2 / Yandex Object Storage) для off-site бэкапов? - **CDN/streaming для replay** — Mux / Cloudflare Stream / своё? Out of scope для MVP, но в плане держим "порт". - **Интеграция со стриминговыми платформами (Twitch)** — out of scope для MVP, но в плане держим "порт". - **Язык CV-сервиса**: Go (gocv) или Python (быстрее итерация, ONNX-рантайм)? Дефолт: Python на PoC для скорости итераций, Go для prod-инференса (меньше ресурсов, проще деплой). - **Конкретные характеристики домашнего сервера** (CPU/RAM/диск/ОС) — нужно для оценки, сколько одновременных гонок он потянет. В PLAN.md пока заложен разумный минимум (4 CPU / 16 GB / 500 GB SSD). - **ЮKassa-аккаунт** — есть ли уже ИП/ООО для приёма платежей, или регистрировать при переходе на биллинг?