diff --git a/docs/RUNBOOK.md b/docs/RUNBOOK.md
new file mode 100644
index 0000000..895f9be
--- /dev/null
+++ b/docs/RUNBOOK.md
@@ -0,0 +1,152 @@
+# Раннбук: автосинк и деплой кабинета
+
+Как устроено скрытие блока «Дополнительные опции» на странице Подписки и вся
+автоматика вокруг него: приватный форк, ежедневный синк с апстримом, сборка
+статики и выкладка на VPS.
+
+Репозиторий: `chillpadclub/bedolaga-cabinet` (приватный форк
+`BEDOLAGA-DEV/bedolaga-cabinet`).
+
+## Суть задачи
+
+На странице `Подписка` нужно было скрыть блок «Дополнительные опции» (докупка
+устройств, трафика, управление серверами), не потеряв возможность и дальше
+получать обновления апстрима — проект активно развивается, жить на статичном
+форке было бы риском самим по себе.
+
+Решение: приватный форк со своим патчем поверх + GitHub Action, который каждый
+день сам подтягивает апстрим, накатывает патч и собирает готовую статику для
+VPS.
+
+## Патч: как спрятан блок
+
+Файл — `src/pages/Subscription.tsx`. Условие рендера обёрнуто в `false &&`, но
+не правкой существующей строки, а **обёрткой снаружи** — так, чтобы ни одна
+оригинальная строка апстрима не менялась, только добавлены две строки до и две
+после:
+
+```tsx
+{/* Additional Options — disabled on purpose */}
+{false && (
+ <>
+{subscription && // ← строка апстрима не тронута
+ (subscription.is_active || subscription.is_limited) &&
+ ...
+
...
+ )}
+ >
+)}
+```
+
+Благодаря этому будущий `git merge` с апстримом конфликтует, только если
+апстрим правит саму первую или последнюю строку блока — а не любую строку
+внутри (докупка устройств, трафика и т.д.), которую вы всё равно не
+используете.
+
+Компоненты `DeviceTopupSheet`, `DeviceReductionSheet`, `TrafficTopupSheet`,
+`ServerManagementSheet` остаются в коде и в сборке — просто не рендерятся. Это
+осознанный выбор в пользу простоты патча, а не баг.
+
+## Как работает автоматика
+
+Один workflow — `.github/workflows/sync-and-build.yml`. Запускается по
+расписанию (ежедневно, 06:00 UTC) и вручную кнопкой `Run workflow`.
+
+1. **Merge апстрима** — `git fetch` + `merge upstream/main` поверх форка.
+2. **Чистка CI** — удаляет `dependabot.yml` и чужие workflow, если merge их
+ принёс.
+3. **Push в форк** — только если merge реально что-то изменил.
+4. **Сборка** — `npm ci` + `npm run build:docker` (те же build-args, что и в
+ официальном образе).
+5. **Релиз** — `tar.gz` статики → GitHub Release с тегом `vX.Y.Z`.
+6. **Уведомление** — сообщение в Telegram: готово или упало.
+
+Если апстрим правит ровно ту же строку, что и патч, — шаг merge падает
+**намеренно**. Чужой код не должен домёрживаться в обход человека. Приходит
+сообщение в Telegram, конфликт разрешается руками локально, затем push — синк
+продолжается.
+
+Сборка и релиз происходят, только если merge реально что-то поменял (или при
+ручном запуске) — вхолостую по расписанию ничего не собирается и не
+публикуется.
+
+## Файлы
+
+| Путь | Назначение |
+|---|---|
+| `.github/workflows/sync-and-build.yml` | Весь пайплайн: синк, чистка, сборка, релиз, уведомления |
+| `scripts/deploy-cabinet.sh` | Деплой на VPS: скачивает релиз, бэкапит текущую статику, раскатывает |
+| `src/pages/Subscription.tsx` | Патч — скрытый блок «Дополнительные опции» |
+| `docs/RUNBOOK.md` | Этот файл |
+
+Удалено из форка сознательно (было в апстриме, плодило шум):
+
+| Файл | Почему убран |
+|---|---|
+| `.github/dependabot.yml` | Открывал десятки PR на бамп версий — оставили только Dependabot alerts (без авто-PR) |
+| `ci.yml`, `lint.yml` | Тесты/линт апстрима — код уже проверен в апстриме до мёржа |
+| `codeql.yml`, `security-audit.yml` | Security-сканы апстрима, дублируют Dependabot alerts |
+| `docker.yml` | Пуш официального образа в GHCR — не нужен, мы публикуем статику |
+| `release-please.yml`, `release.yml` | Автобамп версии апстрима — версию читаем из `package.json` |
+
+## Секреты и токены
+
+| Что | Где хранится | Права |
+|---|---|---|
+| `TG_BOT_TOKEN` | GitHub → Settings → Secrets and variables → Actions | Отправка сообщений ботом |
+| `TG_CHAT_ID` | Там же | — |
+| Fine-grained PAT | VPS: `~/.cabinet-deploy-token`, права файла `600` | Только `Contents: Read-only` на этот репозиторий |
+
+Токен для VPS специально ограничен одним репозиторием и правом только на
+чтение — даже если файл утечёт, им нельзя ничего запушить или изменить.
+
+## Как выкатить на прод
+
+На VPS:
+
+```bash
+./deploy-cabinet.sh # последний релиз
+./deploy-cabinet.sh v1.63.0 # откат/установка конкретной версии
+```
+
+Скрипт сам:
+1. скачивает `cabinet-dist.tar.gz` через `gh release download`;
+2. бэкапит текущую статику в `cabinet-dist.bak`;
+3. раскатывает новую через `rsync -a --delete` (чистит устаревшие
+ хэшированные JS-чанки, не трогает `50x.html`).
+
+Бэкап один, перезаписывается при каждом запуске. Откатиться на конкретную
+версию надёжнее через `./deploy-cabinet.sh vX.Y.Z` (GitHub хранит все
+релизы), а не полагаться только на `.bak`.
+
+## Если что-то пошло не так
+
+**Пришло Telegram-сообщение «⚠️ упал»** — почти всегда конфликт merge с
+апстримом. Локально:
+```bash
+git fetch upstream main && git merge upstream/main
+# разрешить конфликт (скорее всего в месте патча Subscription.tsx)
+git add -A && git commit && git push origin main
+```
+Следующий прогон workflow снова пойдёт сам.
+
+**Деплой на VPS выкатил битую сборку**:
+```bash
+rsync -a --delete cabinet-dist.bak/ cabinet-dist/ # мгновенный откат
+# либо
+./deploy-cabinet.sh vX.Y.Z # конкретный рабочий релиз
+```
+
+**Security-алерт в GitHub про уязвимость** — это Dependabot alerts (без
+авто-PR). Апстрим обычно фиксит сам, фикс прилетит следующим синком. Если
+критично и срочно — можно поправить зависимость руками отдельным коммитом.
+
+## Что стоит проверять периодически
+
+- **Раз в 1–2 месяца — вкладка Actions.** GitHub отключает scheduled-триггер
+ после ~60 дней без активности в репозитории. Один ручной `Run workflow`
+ включает обратно.
+- **Бэк и фронт бота обновлять вместе.** Синк фронта и апдейт бэкенда бота —
+ по-прежнему ручной процесс, специально не автоматизировали дальше деплоя.
+- **CHANGELOG апстрима перед выкаткой** — на случай breaking changes в API
+ между бэкендом и фронтом.