Закрывает Type A эпика ORCH-10 (поверх 10-common ORCH-101). Docs+tests (паттерн ORCH-077/092): src/**, docker-compose.yml, Dockerfile, scripts/** — ноль изменений; конвейер (STAGE_TRANSITIONS/QG_CHECKS/check_*/machine-verdict/ схема БД) — байт-в-байт. - docs/deployment/LITE_SETUP.md (D1/D2): golden source Lite-тиража — 13 нормативных разделов в порядке маршрута оператора, каждый шаг = fenced-команда + явная «Проверка:»/PASS/FAIL, хост-специфика только плейсхолдерами; канон не форкается (статусы/env/вебхуки/smoke — ссылками на ONBOARDING §1 / REPLICATION §2–§4 / SETUP_WEBHOOKS; явно — только fail-closed Confirm Deploy/STOP и обязательные ключи нового хоста). - .env.watchdog.example (D5, исход А-4): третий канонический env-example; key-set = блок WATCHDOG_* .env.example (19 ключей, токены — пустые плейсхолдеры); закрывает ловушку файла-носителя (sidecar читает ТОЛЬКО .env.watchdog); C-1 ORCH-100 + когерентность порта в шапке; .env.watchdog добавлен в .gitignore (секрет-гигиена, зеркало .env.staging). - tests/test_lite_setup_doc.py (D8): 25 структурных тестов без сети/LLM/subprocess — 13 разделов в порядке D2, кирпичи FR-6.1, key-sync watchdog-канона, env-ключи ⊂ .env.example, compose-подмножество (ровно орк+watchdog по дефолту, staging за профилем, анти-появление plane*/gitea*), fenced-скан FORBIDDEN (импорт из test_no_host_hardcodes) + секрет-эвристика с негативным самочеком, «22 статуса» сверкой импорта plane_sync._PLANE_NAME_TO_KEY, перекрёстность. - Перекрёстные доки (FR-7): REPLICATION.md §1 (Type A — Lite → ✅ ORCH-102 + ссылка), README.md (способность Lite + docs/deployment/ в структуре), INFRA.md (.env.watchdog в секрет-нормативе + ссылка на deployment), CLAUDE.md (блок ORCH-102), CHANGELOG.md. Нормативы разделов: Gitea — branch protection на main НЕ включать (D3 / ADR D10 ORCH-009 / INV-4), pre-receive не вводится, ОДИН глобальный webhook-секрет; staging-вилка опциональна (D6); источник кода — параметризованный git clone <ORCHESTRATOR_GIT_URL> (D7); stateless — данные/задачи/секреты боевого хоста НЕ переносятся (AC-3). Тесты: pytest tests/ -q — 1789 passed (полный регресс зелёный). Refs: ORCH-102 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
13 KiB
REPLICATION — тираж платформы на новую инфраструктуру (ORCH-101)
RUNBOOK фундамента тиража (эпик ORCH-10, слой 10-common). Как развернуть оркестратор на чужом хосте без правки кода: переменные → секреты → smoke. Тираж stateless: данные/БД/секреты боевого хоста НЕ переносятся ни на одном шаге — на целевой инфре всё создаётся заново.
1. Границы: 10-common vs Lite vs Bundled
| Слой | Что это | Статус |
|---|---|---|
| 10-common (этот док) | фундамент: все хост-значения параметризованы (env/конфиг), секреты выпускаются заново, smoke-процедура с PASS/FAIL | ✅ ORCH-101 |
| Type A — Lite | инструкция «поставь Plane+Gitea сам, подключи оркестратор» поверх 10-common | ✅ ORCH-102 — docs/deployment/LITE_SETUP.md |
| Type B — Bundled | комплект «всё в одном» (Plane+Gitea+оркестратор) поверх 10-common | отдельная задача эпика |
Этот док НЕ описывает установку Plane/Gitea — только параметризацию, секреты и smoke самого оркестратора (анти-скоуп-крип Р-5).
Платформенные конвенции тиража (нормативно, ADR-001 D3/D4)
- Репо платформы обязан называться
orchestrator. Имя — узел безопасности (SELF_HOSTING_REPO, на него завязаны все*_repos-leaf'ы «empty CSV → self-hosting only»); оно сознательно НЕ конфигурируется. - Имена compose-сервисов/профиля (
orchestrator,orchestrator-staging,orchestrator-watchdog, профильstaging) — константы платформы. - Контейнерные пути (
/app/data,/repos,/opt/claude-code) — layout контейнера, не хост-значения; не параметризуются.
2. Карта переменных нового хоста
Принцип (ADR-001 D1): дефолт каждой переменной = боевое значение текущего
хоста — пустой .env ⇒ поведение байт-в-байт; на новом хосте задаёшь только
то, что отличается. Одно env-имя = один факт: pydantic Settings читает имя из
env_file, compose-интерполяция ${VAR:-default} — из .env проекта/shell
(⚠️ НЕ из env_file сервиса: .env.staging на интерполяцию не влияет —
значения для маунтов/uid/портов живут в .env).
2.1. Хост-параметризация (новое в ORCH-101)
| Переменная | Дефолт | Назначение |
|---|---|---|
ORCH_AGENT_HOME_DIR |
/home/slin |
HOME всех акторов (агенты, finalizer, monitor) + таргет маунтов .claude/.claude.json/.ssh + ARG APP_HOME (группа ORCH-040 двигается вместе) |
ORCH_AGENT_GIT_NAME |
claude-bot |
git-имя коммитов агентов |
ORCH_GIT_EMAIL_DOMAIN |
mva154.local |
домен git-email всех акторов (claude-bot@…, deploy-finalizer@…, post-deploy-monitor@…) |
ORCH_STAGING_PORT |
8501 |
порт staging; читают image_freshness И compose command:; guard: совпадение с прод-портом → отказ fail-closed (ORCH-058 AC-9) |
ORCH_HOST_REPOS_DIR |
/home/slin/repos |
каталог репозиториев на хосте (источник маунта /repos) |
ORCH_HOST_CLAUDE_DIR |
/home/slin/.claude |
источник маунта ~/.claude |
ORCH_HOST_CLAUDE_JSON |
/home/slin/.claude.json |
источник маунта ~/.claude.json |
ORCH_HOST_SSH_DIR |
/home/slin/.orchestrator-ssh |
источник маунта ssh-ключей (→ $HOME/.ssh:ro) |
ORCH_HOST_CLAUDE_CODE_DIR |
/usr/lib/node_modules/@anthropic-ai/claude-code |
дистрибутив claude-code на хосте |
ORCH_HOST_NODE_BIN |
/usr/bin/node |
бинарь node на хосте |
ORCH_RUN_UID / ORCH_RUN_GID |
1000 / 1000 |
uid:gid контейнера (user: + ARG APP_UID/APP_GID); = uid владельца ORCH_HOST_REPOS_DIR (ORCH-040) |
ORCH_DOCKER_GID |
999 |
gid группы docker хоста (group_add, «МИНА 1» — обязателен для docker.sock); узнать: getent group docker |
ORCH_DEPLOY_PROD_TARGET_PORT |
8500 |
(реюз) прод-порт; интерполируется в command: прод-сервиса |
ORCH_DEPLOY_SSH_USER / ORCH_DEPLOY_HOST_REPO_PATH |
slin / /home/slin/repos/orchestrator |
(реюз) ssh-юзер хука и чекаут платформы на хосте; REPO= передаётся хуку явно из конфига |
2.2. Обязательные ключи идентичности нового хоста
| Переменная | Где взять |
|---|---|
ORCH_PLANE_API_URL / ORCH_PLANE_WEB_URL / ORCH_PLANE_WORKSPACE_SLUG |
инсталляция Plane целевого хоста |
ORCH_GITEA_URL / ORCH_GITEA_PUBLIC_URL / ORCH_GITEA_OWNER |
инсталляция Gitea целевого хоста |
ORCH_PROJECTS_JSON |
обязателен на новом хосте: встроенный fallback (src/projects.py) несёт Plane-UUID исходного хоста — чужие UUID безвредны (не сматчатся), но без своего реестра конвейер не увидит проекты. Сгенерировать: scripts/onboard_project.py apply печатает merged-значение |
| Когерентность портов | сменил прод-порт → синхронно ORCH_DEPLOY_PROD_TARGET_PORT ⇄ WATCHDOG_METRICS_URL ⇄ ORCH_POST_DEPLOY_BASE_URL |
Полный справочник всех остальных флагов — .env.example (канон) и
docs/operations/INFRA.md (карта env).
3. Секреты нового хоста (FR-4 / AC-5)
Нормативно: боевые секреты текущего хоста НЕ копируются ни на одном шаге. Для нового хоста всегда выпускается новый комплект.
3.1. Генерация локальных webhook-секретов
python3 scripts/gen_secrets.py # печать .env-фрагмента в stdout
python3 scripts/gen_secrets.py --write # создать .env (существующий → отказ exit=2)
python3 scripts/gen_secrets.py --write --force # перезапись только явно
Скрипт stdlib-only (secrets.token_hex(32) — 32 байта энтропии); повторный
запуск даёт другие значения; существующий .env никогда не перезаписывается
молча.
| Секрет | Генерируется | Куда вписать |
|---|---|---|
ORCH_PLANE_WEBHOOK_SECRET |
локально (gen_secrets) | .env + настройка webhook в Plane (см. SETUP_WEBHOOKS.md) |
ORCH_GITEA_WEBHOOK_SECRET |
локально (gen_secrets) | .env + webhook Gitea (создаёт onboard_project.py apply) |
3.2. Чек-лист внешних токенов
| Секрет | Где выпустить | Куда вписать | Как проверить |
|---|---|---|---|
ORCH_PLANE_API_TOKEN |
Plane UI → Workspace Settings → API tokens | .env |
curl -H "X-API-Key: $TOKEN" $ORCH_PLANE_API_URL/api/v1/workspaces/<slug>/projects/ → 200 |
ORCH_PLANE_BOT_* (7, опциональны) |
Plane UI: bot-аккаунты per-агент; пусто → fallback на ORCH_PLANE_API_TOKEN |
.env |
комментарий в Plane от имени бота |
ORCH_GITEA_TOKEN |
Gitea UI → Settings → Applications → Generate Token (scope: repo, admin:repo_hook) | .env |
curl -H "Authorization: token $TOKEN" $ORCH_GITEA_URL/api/v1/user → 200 |
ORCH_TELEGRAM_BOT_TOKEN |
BotFather (/newbot) |
.env |
curl https://api.telegram.org/bot$TOKEN/getMe → ok |
ORCH_TELEGRAM_CHAT_ID (несекретный) |
id чата оператора | .env |
тестовое сообщение |
WATCHDOG_TG_BOT_TOKEN / WATCHDOG_TG_CHAT_ID |
отдельный бот sidecar-watchdog (ORCH-100, независимый канал) | .env.watchdog |
алерт от sidecar |
3.3. Полнота
.env.example — канон 100% ключей старта (секретные значения — только
плейсхолдеры). Состав вывода gen_secrets.py сверяется с .env.example
тестом (tests/test_secrets_gen.py).
4. Smoke-верификация тиража (FR-5 / AC-3)
Процедура «инстанс → тестовый проект → тестовая задача → конвейер доехал» из
существующих кирпичей; каждый шаг имеет явный PASS/FAIL. Итог — однозначный
вердикт: все шаги PASS ⇒ тираж PASS; любой шаг FAIL ⇒ тираж FAIL (собери логи
контейнера docker logs orchestrator и снапшот GET /queue и разбирайся).
Воспроизводимость без нового железа: процедура прогоняется на текущей инфре —
staging-песочница (порт ORCH_STAGING_PORT, дефолт 8501) + sandbox-проект.
Stateless: ни один шаг не предполагает перенос данных/БД/секретов.
| # | Шаг | Команда | PASS | FAIL |
|---|---|---|---|---|
| 0 | Конфиг резолвится | docker compose config |
резолв без ошибок; при пустом env значения = боевым дефолтам | ошибка интерполяции / неожиданные значения |
| 1 | Инстанс жив | curl -fsS http://127.0.0.1:<port>/health |
HTTP 200, "status":"ok" |
не-200 / таймаут |
| 2 | Контракты отвечают | curl -fsS …/queue и …/metrics |
JSON со штатными блоками; /metrics → schema_version: 1 |
не-JSON / 5xx |
| 3 | Тестовый проект | python3 scripts/onboard_project.py plan → apply → verify (sandbox) |
verify зелёный (статусы/лейблы/repo/webhook на месте) |
verify красный / manual-step не выполнен |
| 4 | Тестовая задача | создать issue в Plane → статус «To Analyse» | задача в БД, analyst-job виден в GET /queue |
задача не появилась (webhook/секрет/реестр) |
| 5 | Минимальный сигнал «конвейер доехал» | дождаться окончания analysis |
артефакты 01–04 в ветке задачи: git ls-tree origin/<branch> docs/work-items/<id>/ |
стадия не завершилась / артефактов нет |
| 6 | Расширенный режим (опционально) | Approved → … → Confirm Deploy | задача дошла до done |
застряла (разбор по GET /queue + логам) |
Ручной запуск deploy-хука на нестандартных портах — всегда с явными
REPO=/TARGET_PORT=(wired-путь оркестратора передаёт их сам из конфига; дефолты хука — staging текущего хоста).
5. Что НЕ переносится (stateless, решение 10.06)
- БД (
data/orchestrator.db) — создаётся пустой при первом старте; .env/.env.staging/.env.watchdog— собираются заново (§2–§3);- worktree'ы/runs/логи — рабочее состояние, не данные;
- боевые секреты — никогда (§3).
RUNBOOK ORCH-101. Поддерживать при добавлении хост-переменных (карта §2 +
.env.example + INFRA.md в том же PR). Анти-регресс возврата хардкодов —
tests/test_no_host_hardcodes.py; параметризация инфра-файлов —
tests/test_infra_parametrization.py.