# BUNDLED_SETUP — Bundled-тираж: весь стек одним комплектом (ORCH-103) > **Golden source Bundled-тиража (Type B эпика ORCH-10).** Маршрут «чистый хост → > работающий конвейер» для заказчика **без собственной инфраструктуры**: один > compose-комплект (`deploy/bundled/docker-compose.yml`) поднимает оркестратор + > watchdog + Gitea + Plane CE, один запуск `scripts/bootstrap_bundle.py apply` > доводит стек до рабочего состояния. Каждый шаг — исполняемая команда + явная > проверка результата (**Проверка:** / PASS / FAIL). Хост-специфика — только > плейсхолдеры `<...>` и `$ENV_VAR`. Тираж **stateless**: данные/задачи/секреты > боевого (исходного) хоста **НЕ переносятся** ни на одном шаге (§12). > Границы слоёв тиража (10-common vs Lite vs Bundled) — `docs/operations/REPLICATION.md` §1; > канон Lite (своя инфраструктура Plane/Gitea) — `docs/deployment/LITE_SETUP.md`. --- ## 1. Рамка Bundled **Что входит в комплект** (compose-проект `orchestrator-bundle`, одна bridge-сеть): - `orchestrator` (конвейер, образ собирается из этого чекаута) и `orchestrator-watchdog` (независимый sidecar-мониторинг); - **Gitea** (git-хостинг, пиннованный официальный образ); - **Plane CE — ≈ 14 контейнеров** (зеркало официального selfhost-комплекта: web/space/admin/api/worker/beat-worker/migrator/live + postgres/redis/ rabbitmq/minio/proxy) — это **ресурсоёмко**, см. §2. **Что НЕ входит** (внешние предусловия заказчика): - **Claude CLI / LLM-доступ** — дистрибутив claude-code, node и аутентификация живут на хосте и пробрасываются маунтами (§8); без них стек поднимется, но конвейер не поедет; - **Telegram-боты** — опциональны (§9): пусто = деградация только нотификаций; - **HTTPS/домены/reverse-proxy** — вне bundle: наружу публикуются три http-порта (§2), терминирование TLS — средствами заказчика. **Bundled vs Lite:** Lite (`LITE_SETUP.md`) подключает оркестратор к **вашим** Plane/Gitea; Bundled везёт их **с собой** на чистых томах. Staging-контур орка в bundle отсутствует вовсе: заказчик Type B эксплуатирует платформу для своих проектов, а не развивает её self-hosting'ом (нужен self-hosting — маршрут Lite, `LITE_SETUP.md` §9.3). Репо `orchestrator` в bundle-инсталляции **не регистрируется** как проект. **Осознанный компромисс (TR-7):** git-доступ агентов — HTTP token-remote (токен бот-юзера в конфиге локальных чекаутов, права 600); ssh-контур сознательно не вводится; порты БД/брокера/minio наружу не публикуются. --- ## 2. Требования к хосту Linux x86_64, один хост. Минимумы проверяет preflight bootstrap **до любых мутаций** (пороги — константы `scripts/bootstrap_bundle.py`, ниже — те же цифры; подтверждаются замером приёмочного развёртывания): | Ресурс | Минимум | Почему | |--------|---------|--------| | RAM | **8 GB** | Plane CE — ≈ 14 контейнеров (миграции и брокер прожорливы) | | Диск | **40 GB** свободно | образы стека + тома postgres/minio/gitea + данные орка | | CPU | **4 vCPU** (рекомендация) | меньше — стек поднимется, но будет медленным | **Карта публикуемых портов** (дефолты; конфигурируемы в `deploy/bundled/.env`, ключи `BUNDLE_*`): | Порт | Ключ | Сервис | |------|------|--------| | 8500 | `BUNDLE_ORCH_PORT` | API оркестратора (`/health`, `/queue`, `/metrics`, вебхуки) | | 8080 | `BUNDLE_PLANE_PORT` | Plane UI (proxy) | | 3000 | `BUNDLE_GITEA_HTTP_PORT` | Gitea web/API | Postgres/redis/rabbitmq/minio наружу **не публикуются** (машинный трафик — внутрисетевой сервис-DNS). ```bash free -g # RAM ≥ 8 GB df -h . # свободно ≥ 40 GB nproc # ≥ 4 ss -ltn | grep -E ':(8500|8080|3000)\b' || echo "ports free" ``` **Проверка:** ресурсы не ниже минимумов и `ports free` — PASS. Порт занят → смените соответствующий `BUNDLE_*`-ключ в §5 (или освободите порт) — иначе preflight откажет (FAIL до мутаций, это штатно). --- ## 3. Предусловия Софт хоста: Docker Engine + Compose v2, git, python3 (+venv), sudo у оператора. ```bash uname -sm # Linux x86_64 docker --version && docker compose version git --version && python3 --version python3 -m venv --help >/dev/null && echo "venv: ok" getent group docker # третье поле — gid, понадобится в §5 (ORCH_DOCKER_GID) id -u && id -g # uid/gid оператора (ORCH_RUN_UID / ORCH_RUN_GID) ``` **Проверка:** все команды отвечают без ошибок, gid группы docker известен — PASS; что-то отсутствует — FAIL (доставьте пакет средствами дистрибутива). --- ## 4. Получение кода Переносится **только код** — чекаут репо `orchestrator` (норматив §12). ```bash git clone <путь-чекаута> cd <путь-чекаута> ls deploy/bundled/docker-compose.yml deploy/bundled/.env.example \ scripts/bootstrap_bundle.py scripts/gen_secrets.py scripts/onboard_project.py ``` **Проверка:** все пять файлов на месте — PASS. Канал дистрибуции (``) согласуйте с поставщиком платформы (как в `LITE_SETUP.md` §3). --- ## 5. Секреты Все секреты инсталляции выпускаются **заново на месте** (§12): webhook-секреты — `scripts/gen_secrets.py`, внутренние креды Plane/Gitea-стека — генерирует bootstrap (в репо — только пустые плейсхолдеры, ни одного дефолтного пароля). **5.1. Конфиг bundle-инфры.** ```bash cd <путь-чекаута> cp deploy/bundled/.env.example deploy/bundled/.env chmod 600 deploy/bundled/.env # заполнить НЕсекретные ключи: BUNDLE_PUBLIC_HOST (IP/имя хоста для браузера), # карту портов BUNDLE_* (§2), ORCH_RUN_UID/ORCH_RUN_GID (из §3), # ORCH_DOCKER_GID (getent group docker, §3), пути Claude CLI (§8). ``` **Проверка:** ```bash docker compose -f deploy/bundled/docker-compose.yml config --quiet && echo "config: PASS" ``` `config: PASS` — интерполяция согласована; ошибка — FAIL (опечатка в `deploy/bundled/.env`). **5.2. Секрет-значения** (пустые ключи `deploy/bundled/.env` и корневого `.env`) заполнит `bootstrap_bundle.py apply` (§7): webhook-секреты — субпроцессом `gen_secrets.py`, креды postgres/rabbitmq/minio/`SECRET_KEY` Plane и пароль админ-бота Gitea — stdlib-генератором. Значения **не печатаются** (только имена ключей); повторный запуск **не перетирает** существующие секреты (явная регенерация — флаг `--force-secrets`, допустим только ДО первого запуска стека). ```bash grep -cE '^(POSTGRES_PASSWORD|SECRET_KEY|RABBITMQ_DEFAULT_PASS|MINIO_ROOT_PASSWORD|GITEA_ADMIN_PASSWORD)=$' \ deploy/bundled/.env ``` **Проверка:** до §7 счётчик `5` (пустые плейсхолдеры) — PASS; после §7 — `0`. --- ## 6. Запуск bundle-compose Одна команда поднимает весь стек (≈ 16 контейнеров; первый запуск тянет образы и гоняет миграции Plane — это минуты, не секунды). ```bash docker compose -f deploy/bundled/docker-compose.yml up -d docker compose -f deploy/bundled/docker-compose.yml ps ``` **Проверка:** все сервисы в состоянии `Up`/`Up (healthy)`; `migrator` — `Exited (0)` (одноразовая миграция) — PASS. Контейнер в рестарт-цикле — FAIL (§14). Шаг идемпотентен; можно пропустить — `bootstrap_bundle.py apply` выполнит `up -d` сам (§7). --- ## 7. Bootstrap Доводка «одним запуском»: preflight → секреты → up/готовность → init Gitea (полностью автоматом: админ-бот + API-токен) → init Plane → онбординг sandbox-проекта **строго** кирпичом `onboard_project.py` (22 канонических статуса, включая fail-closed **`Confirm Deploy`** и **`STOP`**, лейблы, репо+webhook — golden source `docs/operations/ONBOARDING.md` §1) → git-доступ агентов → сборка `.env`/`.env.watchdog` → health. ```bash python3 scripts/bootstrap_bundle.py # план + preflight-диагностика (ноль мутаций) python3 scripts/bootstrap_bundle.py apply # полный прогон ``` **Manual-step чекпоинты Plane CE** (API первичной инициализации в CE нет; каждый чекпоинт: точная инструкция → подтверждение → верификация результата API-пробой, молчаливый пропуск запрещён): 1. **instance setup** — открыть Plane UI, зарегистрировать первого пользователя (станет администратором инстанса); 2. **workspace** — создать workspace, ввести его slug в bootstrap; 3. **API-токен** — Workspace Settings → API tokens, вставить значение в bootstrap (ввод скрыт; уходит в `ORCH_PLANE_API_TOKEN`); 4. **workspace-webhook** — bootstrap регистрирует сам (запись в Postgres инсталляции, путь Б канона `LITE_SETUP.md` §5.4) и проверяет; при отказе — честный ручной шаг с той же проверкой; 5. **порядок статусов на доске** — drag-and-drop по отчёту onboard (`docs/operations/ONBOARDING.md`). Exit-коды: `0` — успех; `2` — остановка на manual-step/предусловии (выполните шаг и перезапустите `apply` — завершённые шаги пропускаются, повторный запуск безопасен); `1` — ошибка. Пароль админ-бота Gitea — ключ `GITEA_ADMIN_PASSWORD` в `deploy/bundled/.env` (права 600; вход в UI Gitea под `GITEA_ADMIN_USERNAME`). **Проверка:** ```bash python3 scripts/bootstrap_bundle.py verify && echo "bootstrap: PASS" ``` `verify` зелёный (health/queue/metrics + onboard verify) — PASS; exit 2 — остались ручные пункты отчёта; exit 1 — FAIL (§14). --- ## 8. LLM (claude CLI) Канон — `LITE_SETUP.md` §7 (полностью применим; не дублируется). Кратко: на хост ставятся claude-code + node, выполняется интерактивный логин CLI; пути прописываются в `deploy/bundled/.env` (это источники маунтов контейнера орка): `ORCH_HOST_CLAUDE_CODE_DIR`, `ORCH_HOST_NODE_BIN`, `ORCH_HOST_CLAUDE_DIR`, `ORCH_HOST_CLAUDE_JSON`. ```bash claude --version docker compose -f deploy/bundled/docker-compose.yml exec orchestrator /usr/bin/claude --version ``` **Проверка:** обе команды печатают версию — PASS; вторая падает — пути в `deploy/bundled/.env` не указывают на фактические каталоги хоста (§14.4). --- ## 9. Telegram Канон — `LITE_SETUP.md` §8 (два независимых бота, C-1: токен орка для watchdog переиспользовать запрещено). Ключи орка (`ORCH_TELEGRAM_BOT_TOKEN`, `ORCH_TELEGRAM_CHAT_ID`) — в корневой `.env`; ключи watchdog (`WATCHDOG_TG_BOT_TOKEN`, `WATCHDOG_TG_CHAT_ID`) — **только** в `.env.watchdog` (файл-носитель, `LITE_SETUP.md` §4.3). Шаг опционален: пустые токены = деградация только нотификаций. ```bash grep -E '^ORCH_TELEGRAM_(BOT_TOKEN|CHAT_ID)=' .env grep -E '^WATCHDOG_TG_(BOT_TOKEN|CHAT_ID)=' .env.watchdog docker compose -f deploy/bundled/docker-compose.yml up -d orchestrator orchestrator-watchdog ``` **Проверка:** ключи заполнены и контейнеры пересозданы → тестовое сообщение от обоих ботов (`getMe` — команды в `LITE_SETUP.md` §8) — PASS; пусто — осознанный PASS без нотификаций. --- ## 10. Онбординг следующих проектов Sandbox-проект создал bootstrap (§7). Каждый следующий проект заказчика — штатный runbook `docs/operations/ONBOARDING.md` поверх bundle-инсталляции; для команд из чекаута: Plane/Gitea доступны на `localhost`-портах §2, webhook-URL — in-network `http://orchestrator:8500/webhook/gitea`. ```bash . .venv/bin/activate # venv создан bootstrap'ом (§7) python3 scripts/onboard_project.py plan \ --name "<имя проекта>" --repo --prefix \ --stack "<стек>" --test-cmd "<команда тестов>" \ --prod-port <порт-прода> --staging-port <порт-staging> \ --webhook-url http://orchestrator:8500/webhook/gitea # план устроил → apply → verify (как в LITE_SETUP.md §10), затем: # строку ORCH_PROJECTS_JSON из отчёта — в .env и пересоздать орк: docker compose -f deploy/bundled/docker-compose.yml up -d --force-recreate orchestrator ``` **Проверка:** `verify` зелёный; `GET /queue` отвечает после пересоздания — PASS. --- ## 11. Smoke Процедура — чек-лист `docs/operations/REPLICATION.md` §4 (шаги 0–5; шаг 6 «до `done`» — опционально) поверх bundle-инсталляции, без форка. Минимальный сигнал «конвейер доехал»: issue в sandbox-проекте Plane → статус **To Analyse** → артефакты `01`–`04` в ветке задачи. ```bash curl -fsS http://127.0.0.1:8500/health curl -fsS http://127.0.0.1:8500/queue | python3 -m json.tool | head -30 curl -fsS http://127.0.0.1:8500/metrics | python3 -m json.tool | head -10 # создать issue в Plane (порт 8080) → перевести в «To Analyse», затем: curl -fsS http://127.0.0.1:8500/queue | python3 -m json.tool | head -40 # job появился git -C deploy/bundled/repos/sandbox fetch origin git -C deploy/bundled/repos/sandbox ls-tree --name-only origin/<ветка-задачи> "docs/work-items//" ``` **Проверка:** оба направления связности живы — job в `/queue` (Plane→орк доехал), `ls-tree` показывает `01-brd.md` … `04-test-plan.yaml` (орк→Gitea пишет; Gitea→орк события идут) — PASS. Любой шаг FAIL → тираж FAIL: соберите `docker compose -f deploy/bundled/docker-compose.yml logs --tail 100 orchestrator` и снапшот `GET /queue`, разбор — §14. (Порты замените, если меняли `BUNDLE_*`.) --- ## 12. Stateless-проверка **Нормативно: данные/задачи/секреты/БД боевого (исходного) хоста НЕ переносятся** (зеркало `docs/operations/REPLICATION.md` §5). Все тома bundle созданы заново при первом `up`; секреты — только свежевыпущенные (§5); в Plane/Gitea инсталляции нет чужих задач/репо/пользователей. ```bash docker volume ls --format '{{.Name}}' | grep '^orchestrator-bundle' # только тома этой инсталляции curl -fsS http://127.0.0.1:8500/queue | python3 -m json.tool # счётчики нулевые ``` **Проверка:** в `/queue` нулевые счётчики и ни одной чужой задачи (никаких work-item исходного хоста) — PASS. Чужая задача/перенесённый файл БД — FAIL: инсталляция собрана не stateless, выполните полный сброс (§13) и повторите. --- ## 13. Остановка и полный сброс Teardown — **только эта документированная процедура** (в bootstrap delete-режима сознательно нет, ADR-001 D9). **Остановка (обратимая):** ```bash docker compose -f deploy/bundled/docker-compose.yml down ``` **Проверка:** `docker compose -f deploy/bundled/docker-compose.yml ps` пуст; тома целы (`docker volume ls | grep orchestrator-bundle`) — PASS. **Полный сброс (НЕОБРАТИМО — удаляет все данные Plane/Gitea/орка):** ```bash docker compose -f deploy/bundled/docker-compose.yml down -v rm -rf deploy/bundled/data deploy/bundled/repos rm -f deploy/bundled/.env .env .env.watchdog ``` **Проверка:** `docker volume ls --format '{{.Name}}' | grep -c '^orchestrator-bundle'` → `0`; live-конфигов нет — PASS (хост чист, можно разворачивать заново с §5). --- ## 14. Траблшутинг Формат: симптом → диагностика → лечение. **14.1. Webhook не доходит (issue в Plane есть, job в `/queue` нет).** ```bash docker compose -f deploy/bundled/docker-compose.yml logs --tail 50 orchestrator | grep -i "webhook\|signature" docker compose -f deploy/bundled/docker-compose.yml exec -T plane-db \ psql -U plane -d plane -c "SELECT url, is_active FROM webhooks;" ``` Лечение: (а) нет строки webhook → §7 чекпоинт 4; (б) URL не `http://orchestrator:8500/webhook/plane` → исправьте на in-network URL; (в) 401/HMAC → секрет в Plane обязан байт-в-байт совпадать с `ORCH_PLANE_WEBHOOK_SECRET` корневого `.env`. Для Gitea-направления проверьте Recent Deliveries в настройках hook'а репо; помните про `GITEA__webhook__ALLOWED_HOST_LIST=orchestrator` в bundle-compose (без него Gitea молча режет вебхуки в приватные адреса). **14.2. Не хватает RAM / OOM (контейнеры Plane в рестарт-цикле).** ```bash free -g && docker stats --no-stream | head -20 docker compose -f deploy/bundled/docker-compose.yml ps ``` Лечение: минимум §2 (8 GB; Plane ≈ 14 контейнеров). Меньше — добавьте RAM/swap; preflight bootstrap отказывает заранее именно поэтому. **14.3. Порт занят (`up` падает с bind error).** ```bash ss -ltnp | grep -E ':(8500|8080|3000)\b' ``` Лечение: смените `BUNDLE_ORCH_PORT`/`BUNDLE_PLANE_PORT`/`BUNDLE_GITEA_HTTP_PORT` в `deploy/bundled/.env` и повторите `up`/bootstrap. **14.4. claude не найден / агент падает на старте.** ```bash docker compose -f deploy/bundled/docker-compose.yml exec orchestrator /usr/bin/claude --version ls "$(grep '^ORCH_HOST_CLAUDE_CODE_DIR=' deploy/bundled/.env | cut -d= -f2)" ``` Лечение: пути `ORCH_HOST_*` в `deploy/bundled/.env` обязаны указывать на фактические каталоги хоста; креды CLI читаемы uid'ом `ORCH_RUN_UID` (канон — `LITE_SETUP.md` §7/§13.3); после правки — `up -d --force-recreate orchestrator`. **14.5. Миграции Plane не завершились (bootstrap падает на ожидании).** ```bash docker compose -f deploy/bundled/docker-compose.yml logs --tail 50 migrator plane-db docker compose -f deploy/bundled/docker-compose.yml ps plane-db plane-mq plane-redis ``` Лечение: чаще всего — нехватка RAM/диска (§14.2) или невыпущенные секреты (пустой `POSTGRES_PASSWORD` → postgres не стартует; прогоните §7, который заполняет креды ДО `up`). После лечения — повторный `apply` (идемпотентен). **14.6. PR задачи не мержится / HOLD.** Branch protection на `main` в Gitea **НЕ включать** — норматив `LITE_SETUP.md` §6.4 (ломает PR-merge API merge-актора); bundle-Gitea конфигурируется тем же правилом. ```bash curl -fsS -H "Authorization: token $ORCH_GITEA_TOKEN" \ "http://127.0.0.1:3000/api/v1/repos///branch_protections" | python3 -m json.tool ``` Лечение: непустой список правил → удалить (канон `LITE_SETUP.md` §6.4/§13.7). --- *Golden source Bundled-тиража (ORCH-103, ADR-001 D10). **Норматив сопровождения (NFR-5):** меняешь шаги Bundled-тиража (состав bundle-compose, ключи `deploy/bundled/.env.example`, шаги bootstrap, smoke) → обнови этот док В ТОМ ЖЕ PR. Полноту и гигиену держит `tests/test_bundled_setup_doc.py`; кирпичи-каноны: `LITE_SETUP.md` (§5–§8 — подключения), `docs/operations/ONBOARDING.md` (статусы §1, онбординг), `docs/operations/REPLICATION.md` (карта env §2, секреты §3, smoke §4), `deploy/bundled/.env.example` + `.env.example` / `.env.watchdog.example` (каноны ключей).*