Files
orchestrator/docs/operations/REPLICATION.md
claude-bot 8351e91382 docs(deployment): ORCH-10a Lite-тираж — LITE_SETUP.md + канон watchdog-конфига + анти-дрейф контур
Закрывает 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>
2026-06-11 00:42:15 +03:00

156 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`](../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-секретов
```bash
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`.*