21 Commits

Author SHA1 Message Date
Porfiry af5dc5db4c ADR-013: разбор редакторной сборки фронта vs Docker/CI — разница в отключённом сжатии; поправка к ADR-012 (оба дефекта есть и в 29) 2026-09-15 19:56:29 +03:00
Porfiry b9799bf091 ADR-012: именованные сборки (zip) рядом с номерными — /sol/; ^~ для служебных путей; два дефекта сборки фронтендера 2026-09-15 19:29:54 +03:00
Porfiry 5087c67201 fix(nginx-152): корень редиректит на активную сборку — закрыта кэш-мина (ассеты Unity зовутся относительным путём)
- index.html: buildUrl="Build" -> без редиректа ассеты шли как /Build/WebGL.wasm (без номера сборки)
  под immutable 7д -> браузер неделю отдавал старую сборку
- publish-release.sh --activate пишет /etc/nginx/game-active.inc: return 302 /<N>/
- immutable сужен до путей с номером сборки; остальное no-store
- приёмка: / -> 302 /29/, браузер тянет /29/Build/* (все 200), autoclose-check 9/9, testfront цел
- сборка 29 (fix webgl: дым падения и PC URP) опубликована на nginx
2026-09-15 18:34:12 +03:00
Porfiry f105c1adff ADR-011: статика игры вынесена из куба на nginx (LXC 152 game-web), версии каталогами
- корень smart-updown.binom.pw -> nginx на Proxmox (LXC 152, 192.168.76.152)
- /api,/ws -> куб (router PathPrefix на внешнем Traefik VDS), куб не менялся
- версионирование: /opt/game/releases/<N>/, активная = симлинк /opt/game/current
- tools/proxmox-nginx: unpack-release.sh, publish-release.sh, game.nginx.conf, smart-updown.traefik.yaml, README
- приёмка живьём: корень 200 nginx/1.24.0 (Ubuntu), /27/ /28/ 200, /api/state 200 JSON, /api/ws цена, autoclose-check 9/9
- питфоллы: OCI-manifest в skopeo dir-дампе, слэш в конце версии, /solana-rpc мимо PathPrefix /api
2026-09-15 17:07:30 +03:00
Hermes Agent 21a3a37a66 docs: ADR-009/010, карта деплоя, деплой-памятка фронта, задачи фронтендеру
Приёмка и починка 15.09.2026:
- ADR-009: один бэк на оба домена — прод-домен smart-updown.binom.pw переведён на тот же
  TS-шлюз (:8895), что и testfront; Kotlin-бэк (3NWEK) выключен обратимо. Разобраны расхождения
  API игры и шлюза (их должны править во фронте).
- ADR-010: две ловушки деплоя фронта — (1) чарт рендерит только path '/', поэтому helm-релиз
  сносит /api,/ws → вынесены в отдельный ingress smart-updown-api вне helm;
  (2) захардкоженный values.image.tag бьёт Chart.AppVersion → helm upgrade НЕ поднимает новый
  образ (новый RS не создаётся), на домене остаётся старый билд при 'Upgrade complete'.
  Приведена правильная команда (--reuse-values --set image.tag=<N>) и способ ПРОВЕРКИ по кубу
  и домену, а не по helm. Отмечен остаточный риск: Unity-ассеты immutable 7 суток при
  неизменяемых именах файлов → кэш браузера держит старый билд.
- README: карта деплоя (кто где живёт), статус проекта на 15.09, актуализирован блок
  безопасности (SEC-16 — утечка SOL, блокер перед mainnet), Kotlin-бэк помечен выключенным,
  добавлен TS-шлюз как боевой бэк, ссылки на tools/ и k8s/.
- TASKS.md: деплой-памятка фронта + задачи фронтендеру (убрать image.tag, хэш в именах
  ассетов, привести API игры к контракту шлюза).
2026-09-15 12:13:23 +03:00
Hermes Agent 101a4b60a9 docs(sec): SEC-16 — безвозвратная утечка ренты PDA-ставки с кошелька игрока
Найдено при приёмке боевого домена smart-updown.binom.pw (симптом «SOL не обновляется»).

- SEC-16: при create_bet ренту PDA-аккаунта ставки (1 559 040 lamports = (96+32+128)*6960)
  платит БЕТТОР, а не шлюз; баланс шлюза-админа при этом не меняется (замер whose-fee.cjs).
  Аккаунт не закрывается никогда (в контракте 9ALs четыре инструкции, ни одной close = bettor)
  → рента теряется безвозвратно. В цепи 75 аккаунтов = 0.116928 SOL заперто, растёт линейно.
  Вердикт: на тестах терпимо (маскируется краном, 5 SOL = ~3207 раундов), перед mainnet — блокер.
- TASKS.md: фича «если цена не изменилась — подождать ещё 15 с» (НЕ доделана), заметка про
  боевой домен на общем TS-шлюзе и лаг крана 8–13 с.
- tools: доказательные скрипты (whose-fee, bet-pda-rent, faucet-latency, sol-balance-cycle,
  settle-math-test, backend-full-test) + README с питфоллами замера.
- k8s/smart-updown-api-ingress.yaml: /api,/ws вынесены вне helm (релиз игры их сносил).
2026-09-15 05:57:34 +03:00
subochev b8a1f8dc39 docs(sec): SEC-9 закрыт — возврат залога при ничьей
Сводка: открыто 8 (было 9), закрыто 8. SEC-9 перенесён в закрытые с полной
историей: причина (close_bet.rs:66), замер доли ничьих (33.6%), решение
владельца (правка контракта), состав правок, собственная мутационная приёмка,
деплой и живой эффект (залипшие #0/#1 закрылись сами, ваулт −1000).
Хвост SEC-8 о «вечном залипании» помечен как устранённый.

Инструменты приёмки:
- tie-refund-accept.cjs — ставки циклом, проверка статуса на цепи
  (живой прогон: 6 возвратов из 25 ставок)
- tie-distribution.cjs, policy-economics.cjs — выбор политики ничьей
- vault-check.cjs — разбор Bet-аккаунтов сырыми байтами
2026-09-14 02:19:28 +03:00
subochev 6ee30c39fb docs(security): SEC-2/SEC-3/SEC-8 закрыты, SEC-4 усилен (updown-relayer:6)
- SEC-3 закрыт: ручка POST /close удалена, ставки закрывает сам шлюз (404)
- SEC-8 закрыт: авто-закрытие + одна открытая ставка на адрес (409)
- SEC-2 закрыт по факту: планировщик settle.ts работает, проверено живьём
- SEC-4 усилен: пара UP+DOWN больше не набирается (вторя ставка -> 409)
- SEC-5 частично: счётчик больше не накрутить массово, но /bet без авторизации
- tools/autoclose-check.cjs: сквозной живой тест (9/9 пройдено)
2026-09-13 18:49:37 +03:00
subochev 6e7132e935 docs(security): SEC-1, SEC-2, SEC-4 закрыты по сути — цена закрытия из истории
Механизм: шлюз берёт цену закрытия из локальной истории на момент expire_time
ставки (priceHistory, node:sqlite, ретеншен 24ч), а не текущую. Момент клика
перестаёт влиять на исход — окно выбора времени закрыто.

Живая проверка (ставка №10, релиз updown-relayer:3):
  цена на момент экспирации 9977000000
  текущая цена в момент клика 9976000000
  exitPrice в транзакции 9977000000  -> взята история, не 'сейчас'
  статус payout_done

Также: tools/ — скрипты проверки (price-history-probe, exploit-decisive,
exploit-check, check-close-window.sh).

Остаток: в контракте верхней границы окна по-прежнему нет (второй слой, не критично);
серверное авто-закрытие — следующая итерация.
2026-09-13 14:47:21 +03:00
subochev 4caddd6a73 docs(security): SEC-6 ЗАКРЫТ — гейт test/release + genesis, начисление вынесено из /bet
Проверено на живом стенде (релизы updown-relayer:2, test-front:3):
- /bet без address -> 400, ничего не начисляет
- /bet на непополненный кошелёк -> 400 insufficient funds
- полный круг POST /wallet -> GET /faucet -> POST /bet -> 200
- smart.mode=release -> 403 'faucet is disabled: only test mode'
- npm test 24/24, мутационная приёмка: ослабление проверки кластеров роняет 2 теста

Остаточный риск: ручка публично маршрутизируется; в бой — mode=release.
2026-09-13 13:39:09 +03:00
subochev 8ce269c16a docs(security): SEC-12 — цена не валидируется и в неcustodial-бэкенде
Проверено на smart-updown-server: GameService.submitBet не сверяет
payload.entryPrice со своим priceSource, сравнение JSON-подсказки с tx
лишь логирует log.warn и не отклоняет. Клиент подписывает entry_price сам.

Зафиксирована инверсия: custodia-шлюз стенда эту дыру закрывает (цена из
NATS на сервере), а неcustodial прод-бэкенд открыт. Переход на неcustodial
без серверной сверки entry_price ухудшит положение.
2026-09-13 13:11:00 +03:00
subochev 5b47de0870 docs(security): SEC-6 — назначение ручки vs гарантия её отсутствия на проде
Зафиксировано разделение: ручка тестовая по назначению (faucet-TASK.md) и на
проде не нужна, однако проверка сети в коде отсутствует, а дефолт
SMART_ALLOW_AIRDROP='1' (config.ts) — открыт. Значит 'не нужна' держится
памятью деплойщика, а не кодом. Фикс: дефолт '0' + getGenesisHash() в handleFaucet.
2026-09-13 12:54:08 +03:00
subochev 08ea780f7d docs(security): SEC-6 — уточнено, что фантиковая часть не localnet-only
Разделены два механизма ручки /faucet:
(а) SOL-налив (requestAirdrop) — технически только localnet, mainnet-RPC
    отдаёт 410 (проверено вживую);
(б) налив фантиков (mintTo) — НЕ привязан к сети, гейт только конфиг-флаг
    SMART_ALLOW_AIRDROP, проверки кластера в коде нет;
    SOL-барьер обходится: при балансе >=2 SOL airdrop не зовётся (ops.ts:204)
    и выполнение доходит прямо до mintTo.
Отмечено, что на боевом мемкойне pump.fun (revoked mint authority) часть (б)
исчезает сама.
2026-09-13 12:46:11 +03:00
subochev 1d49468920 docs(security): нумерация пунктов чек-листа SEC-1..SEC-15
Сквозные номера не сбиваются при добавлении/закрытии пунктов внутри
разделов. В SEC-4 фикс ссылается на SEC-1/SEC-2. В сводке — раскладка
по важности.
2026-09-13 12:42:27 +03:00
subochev 18ecbb42b3 docs(security): чек-лист безопасности игрового контура (аудит живого стенда)
15 пунктов, отмечаются [ ]/[x]. Критичное найдено воспроизведением на
testfront.binom.pw, не чтением кода:
- close_bet проверяет только нижнюю границу окна (now >= expire_time) —
  верхней нет, ставка висит open бесконечно, момент расчёта выбирает
  вызывающий /close;
- в шлюзе нет авто-закрытия (единственный setInterval — WS-heartbeat);
- /bet, /close, /faucet открыты наружу без авторизации; faucet выдал
  5 SOL + 10000 фантиков при SMART_ALLOW_AIRDROP=1 в боевом поде;
- эксплойт воспроизведён: ставка DOWN (entry=9998000000) висела open >2 мин
  после экспирации 15с и закрыта при exitPrice=9997000000 -> payout_done.
Плюс: множитель 1.3x требует 77% винрейта, запертые средства в ваулте,
равные цены не финализируют ставку, NATS без auth, релейер — точка доверия.
2026-09-13 12:40:51 +03:00
subochev 30f851fb87 docs: ADR-008 (реализация цены из NATS) + PRICE-NATS.md + PlantUML (фаза 7)
ADR-007 (выбор источника binance-market/NATS) пришёл из другой сессии —
оставлен как есть, с апдейт-сноской. Реализация зафиксирована отдельным
ADR-008: NATS-консьюмер в шлюзе, цена x1e8 строковым парсером, раздача
фронту по WebSocket, деплой в k3s на testfront.binom.pw.
2026-09-13 05:58:05 +03:00
subochev 6e54e9ef4b ADR-007: источник цены SOL = upstream binance-market (NATS / WS)
- README: новая секция «Внешние зависимости» — binance-market как общий
  инфраструктурный upstream, а не часть игры (~/common-projects/invest/binance-market,
  контракт: NATS market.price.solusdt или WS /ws/price).
- docs/DECISIONS: добавлен ADR-007 с обоснованием; ADR-006 получил апдейт-сноску.
- docs/ECONOMY: «Курс и балансы» — указано, откуда берётся реальная цена SOL.
- TASKS: задачи «Цена SOL: реальный источник» (Фаза 0) и «PriceSource: реальная
  цена» (Фаза 2) переведены из TODO в IN_PROGRESS, со ссылкой на ADR-007 и
  binance-market; test-front задача тоже помечена.
2026-09-12 01:10:09 +03:00
Caffeine ba14806e6c docs: FLOW.md — sequence-диаграммы (UML) как ходят данные: сейчас (localnet+шлюз, без Phantom) и боевой (pump.fun+Phantom+подпись игрока) 2026-09-10 05:57:26 +03:00
Caffeine 8ad5b6ca28 ADR-006: предмет ставки = цена SOL (валюта фантики), снять BLOCKED, добавить задачу на реальную цену 2026-09-09 18:26:59 +03:00
Caffeine fa62f6adca Зафиксировать решения ADR-005 (флоу пополнение/вывод), уточнить открытые развилки до Фазы 1 2026-09-09 18:16:10 +03:00
Caffeine ed7d85b3dd Инициализация мета-репо: карта репозиториев, решения (ADR), экономика токена, трекер задач 2026-09-09 18:11:34 +03:00