--- work_item: ORCH-101 stage: architecture author_agent: architect status: proposed created_at: 2026-06-10 model_used: claude-opus-4-8 --- # adr-0036: Фундамент тиража платформы — параметризация хоста, секреты, smoke (ORCH-101, 10-common) ## Статус Proposed ## Контекст Эпик ORCH-10 (D5.3 «Масштаб») — тираж платформы для заказчиков-тестеров двумя типами (A Lite / B Bundled), оба stateless. Платформа была фактически прибита к хосту `mva154`: четыре места в `src/**` обходили конфиг (внешний Gitea-URL в `plane_sync`, HOME + git-идентичности акторов в `launcher`/`self_deploy`/`post_deploy`), `docker-compose.yml`/`Dockerfile`/deploy-hook несли литералы путей/uid/gid/портов; механизма выпуска нового комплекта секретов и процедуры верификации развёрнутой копии не существовало. ORCH-101 (10-common) — общий фундамент обоих типов тиража. Это сквозное решение: оно задаёт **платформенные конвенции тиража** и трогает блоки, помеченные маркерами ORCH-036/ORCH-040/ORCH-058 (по `docs/_standards/TRACEABILITY.md` — сводный ADR вместо архео-перечисления). Детальный пакет решений (D1…D10) — work-item ADR: `docs/work-items/ORCH-101/06-adr/ADR-001-host-parametrization-secrets-smoke.md`. ## Решение **Принцип: «дефолт = боевое значение».** Каждое хост-специфичное значение читается из конфига (`Settings` env `ORCH_*` / compose-интерполяция `${VAR:-default}` / Dockerfile `ARG` / shell-default хука) с дефолтом, равным текущему боевому значению. Отсутствие новых переменных = байт-в-байт текущее поведение (kill-switch-природа; отдельный функциональный флаг не вводится). `src/config.py` и `watchdog/config.py` — единственные легитимные места хост-литералов в коде. **Новые конфиг-ключи:** `agent_home_dir` (`ORCH_AGENT_HOME_DIR`, `/home/slin`) — HOME всех акторских процессов; `agent_git_name` (`claude-bot`) + `git_email_domain` (`mva154.local`) — git-идентичности (`@`; системные акторы `deploy-finalizer`/`post-deploy-monitor` — платформенные литералы); `staging_port` (`ORCH_STAGING_PORT`, `8501`). Ссылки в Plane-комментариях — из существующих `gitea_public_url`/`gitea_owner`. Compose-слой — карта `ORCH_HOST_*`/ `ORCH_DOCKER_GID`/`ORCH_RUN_UID/GID` + реюз `ORCH_DEPLOY_*`; порт прод/стейджинг — явные `command:` с `${ORCH_DEPLOY_PROD_TARGET_PORT:-8500}` / `${ORCH_STAGING_PORT:-8501}` (CMD образа не трогается — exec-form + `init: true` сохранены). **Платформенные конвенции тиража (нормативно):** 1. **`SELF_HOSTING_REPO = "orchestrator"` — константа, не конфиг.** На ней «empty CSV → self-hosting only» всех `*_repos`-leaf'ов; конфигурируемость превращала бы опечатку env в активацию деплой-машинерии на чужом репо или тихое выключение всех self-гейтов. Репо платформы в тираже обязан называться `orchestrator` (REPLICATION.md). 2. **Имена compose-сервисов/контейнеров/образов, профиль `staging`, `network_mode: host`, контейнерный layout (`/app/data`, `/repos`, `/opt/claude-code`)** — конвенции, не переменные (для образов истина уже в конфиге `deploy_prod_*_image`). 3. **Staging-порт конфигурируем ТОЛЬКО с fail-closed guard'ом** (усиление инварианта ORCH-058 AC-9): freshness-путь отказывает ДО любого ssh/build при `staging_port == deploy_prod_target_port` — без тихого fallback. Explicit-pass таргета хуку (`TARGET_PORT=` и др.) сохранён; добавлена явная передача `REPO=` обоими инвокерами хука (его строка 38 становится `"${REPO:-…}"` — exit-контракт 0/1/2 ORCH-036 не тронут). 4. **Группа ORCH-040 неделима:** uid/gid/HOME/маунт-таргеты/`useradd` управляются одними env насквозь (`ORCH_RUN_UID/GID`, `ORCH_AGENT_HOME_DIR` → compose `user:`/таргеты/`build.args APP_*`); `group_add` docker-gid («МИНА 1») не удаляется — литерал станет `${ORCH_DOCKER_GID:-999}`. **Секреты нового хоста:** stdlib-скрипт `scripts/gen_secrets.py` — криптослучайные webhook-секреты (`secrets.token_hex(32)`), печать по умолчанию, `--write` отказывает при существующем `.env` (перезапись — только явный `--force`); внешние токены (Plane/Gitea/Telegram/watchdog) — по чек-листу. Норматив: **боевые секреты текущего хоста не копируются ни на одном шаге**. **Smoke-верификация тиража:** runbook `docs/operations/REPLICATION.md` (deployment golden source: карта env, чек-лист секретов, пошаговый smoke с PASS/FAIL: `compose config` → `/health` → `/queue`+`/metrics` → `onboard_project.py plan/apply/verify` → тестовая задача → артефакты `01–04` в ветке; расширенно — до `done`; границы 10-common vs Lite vs Bundled). Нового smoke-скрипта нет — шаги собраны из существующих кирпичей. **Анти-регресс (постоянная CI-гарантия):** структурный сканер `tests/test_no_host_hardcodes.py` — запрещённые литералы (`82.22.50.71`, `/home/slin`, `mva154`, `duckdns`; список централизован) в исполняемом коде `src/**`+`watchdog/**`; `tokenize`-исключение комментариев/докстрингов; структурное исключение двух config-модулей (канон дефолтов); allowlist пуст; негативная самопроверка. ### Что НЕ меняется `STAGE_TRANSITIONS`, состав `QG_CHECKS`, семантика `check_*`, machine-verdict ключи, схема БД — байт-в-байт; значения существующих конфиг-дефолтов; INV-4; прод-контейнер в рамках задачи не рестартуется (правки compose/Dockerfile инертны до штатного деплоя через staging 8501 → `Confirm Deploy`). ## Альтернативы - **`ORCH_SELF_HOSTING_REPO` конфигом** — отвергнуто: узел безопасности; опечатка = групповой риск. - **Staging-порт константой** — отвергнуто: compose-порт параметризуется (AC-6), константа дала бы рассинхрон слоёв; пара «ключ + guard» строго сильнее. - **Smoke-скрипт-обвязка / генератор в `onboard_project.py`** — отвергнуто: лишняя поверхность; разные жизненные циклы (онбординг проекта ≠ provisioning хоста). ## Последствия - Платформа разворачивается на чужой инфре env-конфигурацией; критический путь ORCH-10 разблокирован (Lite/Bundled строятся поверх REPLICATION.md). - Инвариант ORCH-058 переходит из «подразумеваемого константой» в исполняемый guard; возврат хост-хардкода ломает CI структурно. - Цена: ~13 новых env-имён (на текущем хосте настраивать нечего — дефолты боевые) и правило «интерполяция читает `.env`/shell, не `env_file`» (зафиксировано в REPLICATION.md). - Откат: не задавать переменные (дефолты = прежнее поведение); полный — revert PR (без миграций). ## Связи adr-0005 (ORCH-040 — uid/HOME/«МИНА 1»; группа становится параметризуемой, инвариант сохранён), adr-0008 (ORCH-058 — INV-FRESH/AC-9; guard усиливает), adr-0007 (ORCH-036 — exit-контракт хука не тронут), adr-0035 (ORCH-009 — onboarding переиспользуется smoke-процедурой; kit не форкается), adr-0001 (`is_self_hosting_repo` — конвенция имени закреплена). Детально — `docs/work-items/ORCH-101/06-adr/ADR-001-host-parametrization-secrets-smoke.md` (D1…D10), `07-infra-requirements.md`, `10-tech-risks.md`.