# Чеклист запуска в прод

Результат предзапускного аудита от 2026-07-02. Кодовая база в хорошем состоянии:
все 510 тестов зелёные (4 сьюта), синтаксис чист на PHP 8.4, находки A–G
архитектурного ревью (`ARCHITECTURE_REVIEW.md`) закрыты, история git не содержит
реальных секретов. Ниже — что нужно сделать **руками при деплое** (код это
проверить не может) и как запускаться поэтапно.

---

## 1. Блокеры — сделать ДО первого запуска

- [ ] **Сменить секреты.** Задай env-переменные (предпочтительно — секреты не
  живут в git-отслеживаемом файле) или отредактируй `config.php`:
  ```
  HA_DASHBOARD_PASSWORD=$(openssl rand -hex 16)   # пароль дашборда
  HA_CRON_SECRET=$(openssl rand -hex 32)          # токен для bot.php?token=...
  ```
  С заводскими значениями `bot.php` и dashboard откажутся работать (guard), но
  проверь это сам: открой dashboard без env — должен быть FATAL.

- [ ] **Вынести чувствительные файлы за web-root.** `wallets.json` / `pools.json`
  содержат вебхук-ключи открытым текстом. Задай
  `HA_PRIVATE_DIR=/home/USER/hyperanalytics_private` (за пределами docroot) —
  при первом запуске файлы перенесутся туда автоматически.

- [ ] **Если сервер nginx (или Apache с `AllowOverride None`)** — `.htaccess` не
  действует! Варианты (см. README, раздел «Безопасность»):
  - лучший: docroot указывает на `dashboard/`, весь остальной код вне web-root;
  - либо deny-правила в конфиге сайта для `config.php`, `*.json`, `*.db*`,
    `*.log`, `*.md`, каталогов `state/ logs/ storage/ hyperliquid/ tests/`.

- [ ] **Проверить снаружи, что ничего не отдаётся.** После деплоя выполнить
  с другой машины — все должны вернуть 403/404:
  ```
  curl -i https://ДОМЕН/config.php
  curl -i https://ДОМЕН/logs/bot.db
  curl -i https://ДОМЕН/state/wallets.json
  curl -i https://ДОМЕН/.git/config
  curl -i https://ДОМЕН/README.md
  ```

- [ ] **HTTPS обязателен.** Сессионная cookie ставится с флагом `secure` только
  под HTTPS; по голому HTTP пароль дашборда уходит открытым текстом.

- [ ] **Для direct-режима — только agent key.** Основной приватный ключ на
  сервер не кладётся никогда. Сгенерируй агента через
  `php approve_agent.php` (основной ключ вводится в STDIN, локально), на сервер —
  только `HL_AGENT_PRIVATE_KEY` + `HL_ACCOUNT_ADDRESS`. Агент не может выводить
  средства — его утечка не теряет депозит.

## 2. Права и cron

- [ ] Каталоги `state/`, `logs/` (и `HA_PRIVATE_DIR`) должны быть **записываемы**
  пользователем PHP (и cron-пользователем, если это разные пользователи —
  иначе SQLite/локи будут конфликтовать по правам).
- [ ] Cron раз в минуту: `* * * * * /usr/bin/php /path/to/bot.php`
  (или внешний HTTP-cron на `bot.php?token=...`). От двойного запуска защищает
  flock-лок, но держи `CYCLE_COUNT × CYCLE_INTERVAL_SEC ≤ 60`.
- [ ] Первый запуск — руками: `php bot.php`, посмотреть `logs/bot.log` на ошибки.

## 3. Поэтапный запуск (не включать всё сразу)

- [ ] **Этап 1 — бумага.** `executor_mode=mock`, `execution_mode=webhook`
  (дефолты). Добавить кошельки, погонять несколько дней, сверить PnL/сигналы.
- [ ] **Этап 2 — testnet.** Для direct: `HL_IS_MAINNET=false`, обкатать
  открытие/закрытие/SL/TP/реверс на тестнете.
- [ ] **Этап 3 — mainnet, малые размеры.** Малый `sizing_fixed_usd` /
  `scale_factor`, **stop_loss включён** для каждой direct-сущности.
- [ ] На общем аккаунте — только **одна** direct-сущность, либо каждой задать
  свой `hl_subaccount` (иначе «одна позиция на символ на аккаунт» даст
  расхождение бумага/биржа; см. §2 и §7 ARCHITECTURE_REVIEW.md).
- [ ] `hl_manage_foreign_positions=false` (дефолт) — бот не тронет позиции,
  открытые руками на том же аккаунте.

## 4. Эксплуатация

- [ ] Мониторинг heartbeat: dashboard показывает состояние cron; полезен внешний
  uptime-монитор на страницу логина (не на `bot.php?token=` — не светить токен
  в стороннем сервисе).
- [ ] **Бэкапы**: `logs/bot.db` (вся история сделок/позиций — в .gitignore, git
  её НЕ хранит) и `wallets.json`/`pools.json` из `HA_PRIVATE_DIR`.
  SQLite в WAL-режиме — бэкапить через `sqlite3 bot.db ".backup ..."`, не cp на живой базе.
- [ ] Ротация логов встроена (5 MB × 3 файла) — ничего делать не нужно.
- [ ] В логе `bot.log` грепать по слову `БЕЗ ЗАЩИТЫ` / `ERROR` первое время
  ежедневно: так сигнализируется позиция, оставшаяся без SL/TP после сбоя.

## 5. Что уже проверено аудитом (делать ничего не надо)

- Тесты: `run.php` 266, `dashboard_test.php` 125, `hl_executor_test.php` 81,
  `hyperliquid_signing_test.php` 38 — все зелёные; `php -l` чист (PHP 8.4).
- Guard от заводских секретов — и в `bot.php`, и в dashboard (`auth.php`).
- Аутентификация: strict session mode, httponly/samesite cookie,
  `session_regenerate_id` после логина, CSRF-токены на всех POST-обработчиках,
  брутфорс-троттлинг по IP (5 попыток / 5 минут lockout), `hash_equals`.
- `require_auth()` на всех страницах dashboard; вывод экранируется
  (`htmlspecialchars`).
- `.htaccess` deny во всех служебных каталогах + запрет `config.php`,
  `*.json|db|log|md` в корне (актуально для Apache/LiteSpeed).
- Надёжность состояния: атомарная запись конфигов (tmp + rename), flock-локи
  (глобальный + per-wallet), SQLite WAL + busy_timeout + retry на BUSY/LOCKED.
- Исполнение: exchange-запросы не ретраятся (нет риска двойного ордера),
  межпроцессный монотонный nonce, SL/TP-цены округляются к тик-сайзу, сверочный
  `getPosition` при неоднозначном ответе биржи, повтор только защитных ордеров
  при сбое брекета, реконсиляция «биржа → учёт» после внешнего SL/TP.
- История git: реальные секреты никогда не коммитились (только плейсхолдеры).
